shared_writable_dirs: | |
- /labs/tiles | |
- /lib/staticmaplite/cache | |
php_extensions: [pgsql, pdo, pdo_pgsql, curl] | |
/labs/tiles/12 | |
/labs/tiles/13 | |
/labs/tiles/14 | |
/labs/tiles/15 | |
/labs/tiles/16 | |
/labs/tiles/17 | |
/labs/tiles/19 | |
/nbproject/private/ |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
include ('include/common.inc.php'); | include ('include/common.inc.php'); |
include_header("About", "about") | include_header("About", "about") |
?> | ?> |
<p> | <p> |
Busness Time - An ACT bus timetable webapp<br /> | Busness Time - An ACT bus timetable webapp<br /> |
Based on the maxious-canberra-transit-feed (<a | Based on the maxious-canberra-transit-feed (<a |
href="http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip">download</a>, | href="http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip">download</a>, |
last updated <?php | last updated <?php echo date("F d Y.", @filemtime('cbrfeed.zip')); ?>)<br /> |
echo date("F d Y.", @filemtime('cbrfeed.zip')); ?>)<br /> | Source code for the <a |
Source code for the <a | href="https://github.com/maxious/ACTBus-data">transit |
href="https://github.com/maxious/ACTBus-data">transit | feed</a> and <a href="https://github.com/maxious/ACTBus-ui">this |
feed</a> and <a href="https://github.com/maxious/ACTBus-ui">this | site</a> available from github.<br /> |
site</a> available from github.<br /> | Uses jQuery Mobile, PHP, PostgreSQL, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service<br /> |
Uses jQuery Mobile, PHP, PostgreSQL, OpenTripPlanner, OpenLayers, OpenStreetMap, Cloudmade Geocoder and Tile Service<br /> | |
<br /> | |
Feedback encouraged; contact maxious@lambdacomplex.org<br /> | |
<br /> | <br /> |
Some icons by Joseph Wain / glyphish.com<br /> | Feedback encouraged; contact maxious@lambdacomplex.org<br /> |
<br /> | <br /> |
<small>Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip. | Some icons by Joseph Wain / glyphish.com<br /> |
Whilst every effort has been made to ensure the high quality and accuracy of the Site, the Author makes no warranty, | Native clients also available for iPhone(<a href="http://itunes.apple.com/au/app/cbrtimetable/id444287349?mt=8">cbrTimetable by Sandor Kolotenko</a> |
express or implied concerning the topicality, correctness, completeness or quality of the information, which is provided | , <a href="http://itunes.apple.com/au/app/act-buses/id376634797?mt=8">ACT Buses by David Sullivan</a>) |
"as is". The Author expressly disclaims all warranties, including but not limited to warranties of fitness for a particular purpose and warranties of merchantability. | and Android (<a href="https://market.android.com/details?id=com.action">MyBus 2.0 by Imagine Team</a>) |
All offers are not binding and without obligation. The Author expressly reserves the right, in his discretion, to suspend, | <br /> |
change, modify, add or remove portions of the Site and to restrict or terminate the use and accessibility of the Site | GTFS-realtime API; |
without prior notice. </small> | Alerts and Trip Updates (but only Cancelled or Stop Skipped) |
<?php | Default format binary but can get JSON by adding ?ascii=yes |
include_footer(); | <br /> |
?> | <br /> |
<small>Disclaimer: The content of this website is of a general and informative nature. Please check with printed timetables or those available on http://action.act.gov.au before your trip. | |
Whilst every effort has been made to ensure the high quality and accuracy of the Site, the Author makes no warranty, | |
express or implied concerning the topicality, correctness, completeness or quality of the information, which is provided | |
"as is". The Author expressly disclaims all warranties, including but not limited to warranties of fitness for a particular purpose and warranties of merchantability. | |
All offers are not binding and without obligation. The Author expressly reserves the right, in his discretion, to suspend, | |
change, modify, add or remove portions of the Site and to restrict or terminate the use and accessibility of the Site | |
without prior notice. </small> | |
<?php | |
include_footer(); | |
?> | |
#!/bin/bash | #!/bin/bash |
#this script should be run from a fresh git checkout from github | #this script should be run from a fresh git checkout from github |
#ami base must have yum install lighttpd-fastcgi, git, tomcat6 | #ami base must have yum install lighttpd-fastcgi, git, tomcat6 |
#php-cli php-gd tomcat6-webapps tomcat6-admin-webapps svn maven2 | #php-cli php-gd tomcat6-webapps tomcat6-admin-webapps svn maven2 |
#postgres postgres-server php-pg | #postgres postgres-server php-pg |
#http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12 | #http://www.how2forge.org/installing-lighttpd-with-php5-and-mysql-support-on-fedora-12 |
cp /root/aws.php /tmp/ | sh busuiphp.sh |
mkdir /var/www/lib/staticmaplite/cache | sh busuidb.sh |
chcon -h system_u:object_r:httpd_sys_content_t /var/www | sh busuiotp.sh |
chcon -R -h root:object_r:httpd_sys_content_t /var/www/* | |
chcon -R -t httpd_sys_content_rw_t /var/www/lib/staticmaplite/cache | |
chmod -R 777 /var/www/lib/staticmaplite/cache | |
chcon -R -t httpd_sys_content_rw_t /var/www/labs/tiles | |
chmod -R 777 /var/www/labs/tiles | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ | |
-O /var/www/cbrfeed.zip | |
createdb transitdata | |
createlang -d transitdata plpgsql | |
psql -d transitdata -f /var/www/lib/postgis.sql | |
# curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz | |
#made with pg_dump transitdata | gzip -c > transitdata.cbrfeed.sql.gz | |
gunzip /var/www/transitdata.cbrfeed.sql.gz | |
psql -d transitdata -f /var/www/transitdata.cbrfeed.sql | |
#createuser transitdata -SDRP | |
#password transitdata | |
#psql -d transitdata -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\" | |
php /var/www/updatedb.php | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ | |
-O /tmp/Graph.obj | |
rm -rfv /usr/share/tomcat6/webapps/opentripplanner* | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-webapp.war | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-api-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-api-webapp.war | |
/etc/init.d/tomcat6 restart | |
createdb transitdata | |
createlang -d transitdata plpgsql | |
psql -d transitdata -f /var/www/lib/postgis.sql | |
# curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz | |
#made with pg_dump transitdata | gzip -c > transitdata.cbrfeed.sql.gz | |
gunzip /var/www/transitdata.cbrfeed.sql.gz | |
psql -d transitdata -f /var/www/transitdata.cbrfeed.sql | |
#createuser transitdata -SDRP | |
#password transitdata | |
#psql -d transitdata -c "GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;" | |
#psql -d transitdata -c "GRANT SELECT,INSERT ON TABLE myway_observations,myway_routes,myway_stops,myway_timingdeltas TO transitdata;" | |
#psql -d transitdata -c "GRANT SELECT,INSERT,UPDATE ON TABLE myway_routes,myway_stops TO transitdata;" | |
##psql -d transitdata -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO transitdata;" | |
php /var/www/updatedb.php |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ | |
-O /tmp/Graph.obj | |
/etc/init.d/tomcat6 stop | |
rm -rfv /usr/share/tomcat6/webapps/opentripplanner* | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-webapp.war | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/opentripplanner-api-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-api-webapp.war | |
/etc/init.d/tomcat6 restart | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/testing/Graph.obj \ | |
-O /tmp/Graph.obj | |
/etc/init.d/tomcat6 stop | |
rm -rfv /usr/share/tomcat6/webapps/opentripplanner* | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/testing/opentripplanner-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-webapp.war | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/testing/opentripplanner-api-webapp.war \ | |
-O /usr/share/tomcat6/webapps/opentripplanner-api-webapp.war | |
/etc/init.d/tomcat6 restart | |
cp /root/aws.php /tmp/ | |
chmod 777 /var/cache/lighttpd/compress/ | |
chcon -h system_u:object_r:httpd_sys_content_t /var/www | |
chcon -R -h root:object_r:httpd_sys_content_t /var/www/* | |
chcon -R -t httpd_sys_content_rw_t /var/www/labs/tiles | |
chmod -R 777 /var/www/labs/tiles | |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ | |
-O /var/www/cbrfeed.zip | |
<?xml version="1.0" encoding="UTF-8"?> | |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> | |
<!-- Single graph --> | |
<import resource="classpath:org/opentripplanner/api/application-context.xml" /> | |
<bean id="graphBundle" class="org.opentripplanner.model.GraphBundle"> | |
<property name="path" value="/tmp/" /> | |
</bean> | |
</beans> | |
Binary files /dev/null and b/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ
Binary files /dev/null and b/css/images/ui-bg_diagonals-thick_20_666666_40x40.png differ
Binary files /dev/null and b/css/images/ui-bg_flat_10_000000_40x100.png differ
Binary files /dev/null and b/css/images/ui-bg_glass_100_f6f6f6_1x400.png differ
Binary files /dev/null and b/css/images/ui-bg_glass_100_fdf5ce_1x400.png differ
Binary files /dev/null and b/css/images/ui-bg_glass_65_ffffff_1x400.png differ
Binary files /dev/null and b/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png differ
Binary files /dev/null and b/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ
Binary files /dev/null and b/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png differ
Binary files /dev/null and b/css/images/ui-icons_222222_256x240.png differ
Binary files /dev/null and b/css/images/ui-icons_228ef1_256x240.png differ
Binary files /dev/null and b/css/images/ui-icons_ef8c08_256x240.png differ
Binary files /dev/null and b/css/images/ui-icons_ffd27a_256x240.png differ
Binary files /dev/null and b/css/images/ui-icons_ffffff_256x240.png differ
Binary files /dev/null and b/css/images/warning.png differ
/* | |
* jQuery UI CSS Framework 1.8.12 | |
* | |
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
* | |
* http://docs.jquery.com/UI/Theming/API | |
*/ | |
/* Layout helpers | |
----------------------------------*/ | |
.ui-helper-hidden { display: none; } | |
.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } | |
.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } | |
.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } | |
.ui-helper-clearfix { display: inline-block; } | |
/* required comment for clearfix to work in Opera \*/ | |
* html .ui-helper-clearfix { height:1%; } | |
.ui-helper-clearfix { display:block; } | |
/* end clearfix */ | |
.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } | |
/* Interaction Cues | |
----------------------------------*/ | |
.ui-state-disabled { cursor: default !important; } | |
/* Icons | |
----------------------------------*/ | |
/* states and images */ | |
.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } | |
/* Misc visuals | |
----------------------------------*/ | |
/* Overlays */ | |
.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } | |
/* | |
* jQuery UI CSS Framework 1.8.12 | |
* | |
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
* | |
* http://docs.jquery.com/UI/Theming/API | |
* | |
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px | |
*/ | |
/* Component containers | |
----------------------------------*/ | |
.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } | |
.ui-widget .ui-widget { font-size: 1em; } | |
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } | |
.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } | |
.ui-widget-content a { color: #333333; } | |
.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } | |
.ui-widget-header a { color: #ffffff; } | |
/* Interaction states | |
----------------------------------*/ | |
.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } | |
.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } | |
.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } | |
.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } | |
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } | |
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } | |
.ui-widget :active { outline: none; } | |
/* Interaction Cues | |
----------------------------------*/ | |
.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } | |
.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } | |
.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } | |
.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } | |
.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } | |
.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } | |
.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } | |
.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } | |
/* Icons | |
----------------------------------*/ | |
/* states and images */ | |
.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } | |
.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } | |
.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } | |
.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } | |
.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } | |
.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } | |
.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } | |
.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } | |
/* positioning */ | |
.ui-icon-carat-1-n { background-position: 0 0; } | |
.ui-icon-carat-1-ne { background-position: -16px 0; } | |
.ui-icon-carat-1-e { background-position: -32px 0; } | |
.ui-icon-carat-1-se { background-position: -48px 0; } | |
.ui-icon-carat-1-s { background-position: -64px 0; } | |
.ui-icon-carat-1-sw { background-position: -80px 0; } | |
.ui-icon-carat-1-w { background-position: -96px 0; } | |
.ui-icon-carat-1-nw { background-position: -112px 0; } | |
.ui-icon-carat-2-n-s { background-position: -128px 0; } | |
.ui-icon-carat-2-e-w { background-position: -144px 0; } | |
.ui-icon-triangle-1-n { background-position: 0 -16px; } | |
.ui-icon-triangle-1-ne { background-position: -16px -16px; } | |
.ui-icon-triangle-1-e { background-position: -32px -16px; } | |
.ui-icon-triangle-1-se { background-position: -48px -16px; } | |
.ui-icon-triangle-1-s { background-position: -64px -16px; } | |
.ui-icon-triangle-1-sw { background-position: -80px -16px; } | |
.ui-icon-triangle-1-w { background-position: -96px -16px; } | |
.ui-icon-triangle-1-nw { background-position: -112px -16px; } | |
.ui-icon-triangle-2-n-s { background-position: -128px -16px; } | |
.ui-icon-triangle-2-e-w { background-position: -144px -16px; } | |
.ui-icon-arrow-1-n { background-position: 0 -32px; } | |
.ui-icon-arrow-1-ne { background-position: -16px -32px; } | |
.ui-icon-arrow-1-e { background-position: -32px -32px; } | |
.ui-icon-arrow-1-se { background-position: -48px -32px; } | |
.ui-icon-arrow-1-s { background-position: -64px -32px; } | |
.ui-icon-arrow-1-sw { background-position: -80px -32px; } | |
.ui-icon-arrow-1-w { background-position: -96px -32px; } | |
.ui-icon-arrow-1-nw { background-position: -112px -32px; } | |
.ui-icon-arrow-2-n-s { background-position: -128px -32px; } | |
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } | |
.ui-icon-arrow-2-e-w { background-position: -160px -32px; } | |
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } | |
.ui-icon-arrowstop-1-n { background-position: -192px -32px; } | |
.ui-icon-arrowstop-1-e { background-position: -208px -32px; } | |
.ui-icon-arrowstop-1-s { background-position: -224px -32px; } | |
.ui-icon-arrowstop-1-w { background-position: -240px -32px; } | |
.ui-icon-arrowthick-1-n { background-position: 0 -48px; } | |
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } | |
.ui-icon-arrowthick-1-e { background-position: -32px -48px; } | |
.ui-icon-arrowthick-1-se { background-position: -48px -48px; } | |
.ui-icon-arrowthick-1-s { background-position: -64px -48px; } | |
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } | |
.ui-icon-arrowthick-1-w { background-position: -96px -48px; } | |
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } | |
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } | |
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } | |
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } | |
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } | |
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } | |
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } | |
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } | |
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } | |
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } | |
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } | |
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } | |
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } | |
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } | |
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } | |
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } | |
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } | |
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } | |
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } | |
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } | |
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } | |
.ui-icon-arrow-4 { background-position: 0 -80px; } | |
.ui-icon-arrow-4-diag { background-position: -16px -80px; } | |
.ui-icon-extlink { background-position: -32px -80px; } | |
.ui-icon-newwin { background-position: -48px -80px; } | |
.ui-icon-refresh { background-position: -64px -80px; } | |
.ui-icon-shuffle { background-position: -80px -80px; } | |
.ui-icon-transfer-e-w { background-position: -96px -80px; } | |
.ui-icon-transferthick-e-w { background-position: -112px -80px; } | |
.ui-icon-folder-collapsed { background-position: 0 -96px; } | |
.ui-icon-folder-open { background-position: -16px -96px; } | |
.ui-icon-document { background-position: -32px -96px; } | |
.ui-icon-document-b { background-position: -48px -96px; } | |
.ui-icon-note { background-position: -64px -96px; } | |
.ui-icon-mail-closed { background-position: -80px -96px; } | |
.ui-icon-mail-open { background-position: -96px -96px; } | |
.ui-icon-suitcase { background-position: -112px -96px; } | |
.ui-icon-comment { background-position: -128px -96px; } | |
.ui-icon-person { background-position: -144px -96px; } | |
.ui-icon-print { background-position: -160px -96px; } | |
.ui-icon-trash { background-position: -176px -96px; } | |
.ui-icon-locked { background-position: -192px -96px; } | |
.ui-icon-unlocked { background-position: -208px -96px; } | |
.ui-icon-bookmark { background-position: -224px -96px; } | |
.ui-icon-tag { background-position: -240px -96px; } | |
.ui-icon-home { background-position: 0 -112px; } | |
.ui-icon-flag { background-position: -16px -112px; } | |
.ui-icon-calendar { background-position: -32px -112px; } | |
.ui-icon-cart { background-position: -48px -112px; } | |
.ui-icon-pencil { background-position: -64px -112px; } | |
.ui-icon-clock { background-position: -80px -112px; } | |
.ui-icon-disk { background-position: -96px -112px; } | |
.ui-icon-calculator { background-position: -112px -112px; } | |
.ui-icon-zoomin { background-position: -128px -112px; } | |
.ui-icon-zoomout { background-position: -144px -112px; } | |
.ui-icon-search { background-position: -160px -112px; } | |
.ui-icon-wrench { background-position: -176px -112px; } | |
.ui-icon-gear { background-position: -192px -112px; } | |
.ui-icon-heart { background-position: -208px -112px; } | |
.ui-icon-star { background-position: -224px -112px; } | |
.ui-icon-link { background-position: -240px -112px; } | |
.ui-icon-cancel { background-position: 0 -128px; } | |
.ui-icon-plus { background-position: -16px -128px; } | |
.ui-icon-plusthick { background-position: -32px -128px; } | |
.ui-icon-minus { background-position: -48px -128px; } | |
.ui-icon-minusthick { background-position: -64px -128px; } | |
.ui-icon-close { background-position: -80px -128px; } | |
.ui-icon-closethick { background-position: -96px -128px; } | |
.ui-icon-key { background-position: -112px -128px; } | |
.ui-icon-lightbulb { background-position: -128px -128px; } | |
.ui-icon-scissors { background-position: -144px -128px; } | |
.ui-icon-clipboard { background-position: -160px -128px; } | |
.ui-icon-copy { background-position: -176px -128px; } | |
.ui-icon-contact { background-position: -192px -128px; } | |
.ui-icon-image { background-position: -208px -128px; } | |
.ui-icon-video { background-position: -224px -128px; } | |
.ui-icon-script { background-position: -240px -128px; } | |
.ui-icon-alert { background-position: 0 -144px; } | |
.ui-icon-info { background-position: -16px -144px; } | |
.ui-icon-notice { background-position: -32px -144px; } | |
.ui-icon-help { background-position: -48px -144px; } | |
.ui-icon-check { background-position: -64px -144px; } | |
.ui-icon-bullet { background-position: -80px -144px; } | |
.ui-icon-radio-off { background-position: -96px -144px; } | |
.ui-icon-radio-on { background-position: -112px -144px; } | |
.ui-icon-pin-w { background-position: -128px -144px; } | |
.ui-icon-pin-s { background-position: -144px -144px; } | |
.ui-icon-play { background-position: 0 -160px; } | |
.ui-icon-pause { background-position: -16px -160px; } | |
.ui-icon-seek-next { background-position: -32px -160px; } | |
.ui-icon-seek-prev { background-position: -48px -160px; } | |
.ui-icon-seek-end { background-position: -64px -160px; } | |
.ui-icon-seek-start { background-position: -80px -160px; } | |
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ | |
.ui-icon-seek-first { background-position: -80px -160px; } | |
.ui-icon-stop { background-position: -96px -160px; } | |
.ui-icon-eject { background-position: -112px -160px; } | |
.ui-icon-volume-off { background-position: -128px -160px; } | |
.ui-icon-volume-on { background-position: -144px -160px; } | |
.ui-icon-power { background-position: 0 -176px; } | |
.ui-icon-signal-diag { background-position: -16px -176px; } | |
.ui-icon-signal { background-position: -32px -176px; } | |
.ui-icon-battery-0 { background-position: -48px -176px; } | |
.ui-icon-battery-1 { background-position: -64px -176px; } | |
.ui-icon-battery-2 { background-position: -80px -176px; } | |
.ui-icon-battery-3 { background-position: -96px -176px; } | |
.ui-icon-circle-plus { background-position: 0 -192px; } | |
.ui-icon-circle-minus { background-position: -16px -192px; } | |
.ui-icon-circle-close { background-position: -32px -192px; } | |
.ui-icon-circle-triangle-e { background-position: -48px -192px; } | |
.ui-icon-circle-triangle-s { background-position: -64px -192px; } | |
.ui-icon-circle-triangle-w { background-position: -80px -192px; } | |
.ui-icon-circle-triangle-n { background-position: -96px -192px; } | |
.ui-icon-circle-arrow-e { background-position: -112px -192px; } | |
.ui-icon-circle-arrow-s { background-position: -128px -192px; } | |
.ui-icon-circle-arrow-w { background-position: -144px -192px; } | |
.ui-icon-circle-arrow-n { background-position: -160px -192px; } | |
.ui-icon-circle-zoomin { background-position: -176px -192px; } | |
.ui-icon-circle-zoomout { background-position: -192px -192px; } | |
.ui-icon-circle-check { background-position: -208px -192px; } | |
.ui-icon-circlesmall-plus { background-position: 0 -208px; } | |
.ui-icon-circlesmall-minus { background-position: -16px -208px; } | |
.ui-icon-circlesmall-close { background-position: -32px -208px; } | |
.ui-icon-squaresmall-plus { background-position: -48px -208px; } | |
.ui-icon-squaresmall-minus { background-position: -64px -208px; } | |
.ui-icon-squaresmall-close { background-position: -80px -208px; } | |
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } | |
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } | |
.ui-icon-grip-solid-vertical { background-position: -32px -224px; } | |
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } | |
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } | |
.ui-icon-grip-diagonal-se { background-position: -80px -224px; } | |
/* Misc visuals | |
----------------------------------*/ | |
/* Corner radius */ | |
.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } | |
.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } | |
.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } | |
.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } | |
.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } | |
.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } | |
.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } | |
.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } | |
.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } | |
/* Overlays */ | |
.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } | |
.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* | |
* jQuery UI Autocomplete 1.8.12 | |
* | |
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
* | |
* http://docs.jquery.com/UI/Autocomplete#theming | |
*/ | |
.ui-autocomplete { position: absolute; cursor: default; } | |
/* workarounds */ | |
* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */ | |
/* | |
* jQuery UI Menu 1.8.12 | |
* | |
* Copyright 2010, AUTHORS.txt (http://jqueryui.com/about) | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
* | |
* http://docs.jquery.com/UI/Menu#theming | |
*/ | |
.ui-menu { | |
list-style:none; | |
padding: 2px; | |
margin: 0; | |
display:block; | |
float: left; | |
} | |
.ui-menu .ui-menu { | |
margin-top: -3px; | |
} | |
.ui-menu .ui-menu-item { | |
margin:0; | |
padding: 0; | |
zoom: 1; | |
float: left; | |
clear: left; | |
width: 100%; | |
} | |
.ui-menu .ui-menu-item a { | |
text-decoration:none; | |
display:block; | |
padding:.2em .4em; | |
line-height:1.5; | |
zoom:1; | |
} | |
.ui-menu .ui-menu-item a.ui-state-hover, | |
.ui-menu .ui-menu-item a.ui-state-active { | |
font-weight: normal; | |
margin: -1px; | |
} | |
/*! | |
* jQuery Mobile v1.0a4 | |
* http://jquerymobile.com/ | |
* | |
* Copyright 2010, jQuery Project | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
*/ | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. | |
* Note: Code is in draft form and is subject to change | |
*/ | |
/* A | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-a { | |
border: 1px solid #2A2A2A; | |
background: #111111; | |
color: #ffffff; | |
font-weight: bold; | |
text-shadow: 0 -1px 1px #000000; | |
background-image: -moz-linear-gradient(top, | |
#3c3c3c, | |
#111111); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #3c3c3c), | |
color-stop(1, #111111)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#3c3c3c', EndColorStr='#111111')"; | |
} | |
.ui-bar-a, | |
.ui-bar-a input, | |
.ui-bar-a select, | |
.ui-bar-a textarea, | |
.ui-bar-a button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-bar-a .ui-link-inherit { | |
color: #fff; | |
} | |
.ui-bar-a .ui-link { | |
color: #7cc4e7; | |
font-weight: bold; | |
} | |
.ui-body-a { | |
border: 1px solid #2A2A2A; | |
background: #222222; | |
color: #fff; | |
text-shadow: 0 1px 0 #000; | |
font-weight: normal; | |
background-image: -moz-linear-gradient(top, | |
#666666, | |
#222222); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #666666), | |
color-stop(1, #222222)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#222222)')"; | |
} | |
.ui-body-a, | |
.ui-body-a input, | |
.ui-body-a select, | |
.ui-body-a textarea, | |
.ui-body-a button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-a .ui-link-inherit { | |
color: #fff; | |
} | |
.ui-body-a .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-br { | |
border-bottom: rgb(130,130,130); | |
border-bottom: rgba(130,130,130,.3); | |
border-bottom-width: 1px; | |
border-bottom-style: solid; | |
} | |
.ui-btn-up-a { | |
border: 1px solid #222; | |
background: #333333; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #000; | |
background-image: -moz-linear-gradient(top, | |
#555555, | |
#333333); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #555555), | |
color-stop(1, #333333)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#555555', EndColorStr='#333333')"; | |
} | |
.ui-btn-up-a a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-hover-a { | |
border: 1px solid #000; | |
background: #444444; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #000; | |
background-image: -moz-linear-gradient(top, | |
#666666, | |
#444444); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #666666), | |
color-stop(1, #444444)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#666666', EndColorStr='#444444')"; | |
} | |
.ui-btn-hover-a a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-down-a { | |
border: 1px solid #000; | |
background: #3d3d3d; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #000; | |
background-image: -moz-linear-gradient(top, | |
#333333, | |
#5a5a5a); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #333333), | |
color-stop(1, #5a5a5a)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#333333', EndColorStr='#5a5a5a')"; | |
} | |
.ui-btn-down-a a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-up-a, | |
.ui-btn-hover-a, | |
.ui-btn-down-a { | |
font-family: Helvetica, Arial, sans-serif; | |
text-decoration: none; | |
} | |
/* B | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-b { | |
border: 1px solid #456f9a; | |
background: #5e87b0; | |
color: #fff; | |
font-weight: bold; | |
text-shadow: 0 -1px 1px #254f7a; | |
background-image: -moz-linear-gradient(top, | |
#81a8ce, | |
#5e87b0); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #81a8ce), | |
color-stop(1, #5e87b0)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#81a8ce', EndColorStr='#5e87b0')"; | |
} | |
.ui-bar-b, | |
.ui-bar-b input, | |
.ui-bar-b select, | |
.ui-bar-b textarea, | |
.ui-bar-b button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-bar-b .ui-link-inherit { | |
color: #fff; | |
} | |
.ui-bar-b .ui-link { | |
color: #7cc4e7; | |
font-weight: bold; | |
} | |
.ui-body-b { | |
border: 1px solid #C6C6C6; | |
background: #cccccc; | |
color: #333333; | |
text-shadow: 0 1px 0 #fff; | |
font-weight: normal; | |
background-image: -moz-linear-gradient(top, | |
#e6e6e6, | |
#cccccc); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #e6e6e6), | |
color-stop(1, #cccccc)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#e6e6e6', EndColorStr='#cccccc')"; | |
} | |
.ui-body-b, | |
.ui-body-b input, | |
.ui-body-b select, | |
.ui-body-b textarea, | |
.ui-body-b button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-b .ui-link-inherit { | |
color: #333333; | |
} | |
.ui-body-b .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-btn-up-b { | |
border: 1px solid #145072; | |
background: #2567ab; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #145072; | |
background-image: -moz-linear-gradient(top, | |
#4e89c5, | |
#2567ab); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #5f9cc5), | |
color-stop(1, #396b9e)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#4e89c5', EndColorStr='#2567ab')"; | |
} | |
.ui-btn-up-b a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-hover-b { | |
border: 1px solid #00516e; | |
background: #4b88b6; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #014D68; | |
background-image: -moz-linear-gradient(top, | |
#72b0d4, | |
#4b88b6); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #72b0d4), | |
color-stop(1, #4b88b6)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#72b0d4', EndColorStr='#4b88b6')"; | |
} | |
.ui-btn-hover-b a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-down-b { | |
border: 1px solid #225377; | |
background: #4e89c5; | |
font-weight: bold; | |
color: #fff; | |
text-shadow: 0 -1px 1px #225377; | |
background-image: -moz-linear-gradient(top, | |
#396b9e, | |
#4e89c5); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #396b9e), | |
color-stop(1, #4e89c5)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#396b9e', EndColorStr='#4e89c5')"; | |
} | |
.ui-btn-down-b a.ui-link-inherit { | |
color: #fff; | |
} | |
.ui-btn-up-b, | |
.ui-btn-hover-b, | |
.ui-btn-down-b { | |
font-family: Helvetica, Arial, sans-serif; | |
text-decoration: none; | |
} | |
/* C | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-c { | |
border: 1px solid #B3B3B3; | |
background: #e9eaeb; | |
color: #3E3E3E; | |
font-weight: bold; | |
text-shadow: 0 1px 1px #fff; | |
background-image: -moz-linear-gradient(top, | |
#f0f0f0, | |
#e9eaeb); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #f0f0f0), | |
color-stop(1, #e9eaeb)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#f0f0f0', EndColorStr='#e9eaeb')"; | |
} | |
.ui-bar-c, | |
.ui-bar-c input, | |
.ui-bar-c select, | |
.ui-bar-c textarea, | |
.ui-bar-c button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-c { | |
border: 1px solid #B3B3B3; | |
color: #333333; | |
text-shadow: 0 1px 0 #fff; | |
background: #f0f0f0; | |
background-image: -moz-linear-gradient(top, | |
#eeeeee, | |
#dddddd); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #eeeeee), | |
color-stop(1, #dddddd)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#dddddd')"; | |
} | |
.ui-body-c, | |
.ui-body-c input, | |
.ui-body-c select, | |
.ui-body-c textarea, | |
.ui-body-c button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-c .ui-link-inherit { | |
color: #333333; | |
} | |
.ui-body-c .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-btn-up-c { | |
border: 1px solid #ccc; | |
background: #eee; | |
font-weight: bold; | |
color: #444; | |
text-shadow: 0 1px 1px #f6f6f6; | |
background-image: -moz-linear-gradient(top, | |
#fefefe, | |
#eeeeee); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fdfdfd), | |
color-stop(1, #eeeeee)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; | |
} | |
.ui-btn-up-c a.ui-link-inherit { | |
color: #2F3E46; | |
} | |
.ui-btn-hover-c { | |
border: 1px solid #bbb; | |
background: #dadada; | |
font-weight: bold; | |
color: #101010; | |
text-shadow: 0 1px 1px #fff; | |
background-image: -moz-linear-gradient(top, | |
#ededed, | |
#dadada); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #ededed), | |
color-stop(1, #dadada)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ededed', EndColorStr='#dadada')"; | |
} | |
.ui-btn-hover-c a.ui-link-inherit { | |
color: #2F3E46; | |
} | |
.ui-btn-down-c { | |
border: 1px solid #808080; | |
background: #fdfdfd; | |
font-weight: bold; | |
color: #111111; | |
text-shadow: 0 1px 1px #ffffff; | |
background-image: -moz-linear-gradient(top, | |
#eeeeee, | |
#fdfdfd); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #eeeeee), | |
color-stop(1, #fdfdfd)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#fdfdfd')"; | |
} | |
.ui-btn-down-c a.ui-link-inherit { | |
color: #2F3E46; | |
} | |
.ui-btn-up-c, | |
.ui-btn-hover-c, | |
.ui-btn-down-c { | |
font-family: Helvetica, Arial, sans-serif; | |
text-decoration: none; | |
} | |
/* D | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-d { | |
border: 1px solid #ccc; | |
background: #bbb; | |
color: #333; | |
text-shadow: 0 1px 0 #eee; | |
background-image: -moz-linear-gradient(top, | |
#ddd, | |
#bbb); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #ddd), | |
color-stop(1, #bbb)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#dddddd', EndColorStr='#bbbbbb')"; | |
} | |
.ui-bar-d, | |
.ui-bar-d input, | |
.ui-bar-d select, | |
.ui-bar-d textarea, | |
.ui-bar-d button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-bar-d .ui-link-inherit { | |
color: #333; | |
} | |
.ui-bar-d .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-body-d { | |
border: 1px solid #ccc; | |
color: #333333; | |
text-shadow: 0 1px 0 #fff; | |
background: #ffffff; | |
} | |
.ui-body-d, | |
.ui-body-d input, | |
.ui-body-d select, | |
.ui-body-d textarea, | |
.ui-body-d button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-d .ui-link-inherit { | |
color: #333333; | |
} | |
.ui-body-d .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-btn-up-d { | |
border: 1px solid #ccc; | |
background: #fff; | |
font-weight: bold; | |
color: #444; | |
text-shadow: 0 1px 1px #fff; | |
} | |
.ui-btn-up-d a.ui-link-inherit { | |
color: #333; | |
} | |
.ui-btn-hover-d { | |
border: 1px solid #aaa; | |
background: #eeeeee; | |
font-weight: bold; | |
color: #222; | |
cursor: pointer; | |
text-shadow: 0 1px 1px #fff; | |
background-image: -moz-linear-gradient(top, | |
#fdfdfd, | |
#eeeeee); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fdfdfd), | |
color-stop(1, #eeeeee)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fdfdfd', EndColorStr='#eeeeee')"; | |
} | |
.ui-btn-hover-d a.ui-link-inherit { | |
color: #222; | |
} | |
.ui-btn-down-d { | |
border: 1px solid #aaaaaa; | |
background: #ffffff; | |
font-weight: bold; | |
color: #111; | |
text-shadow: 0 1px 1px #ffffff; | |
background-image: -moz-linear-gradient(top, | |
#eeeeee, | |
#ffffff); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #eeeeee), | |
color-stop(1, #ffffff)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#eeeeee', EndColorStr='#ffffff')"; | |
} | |
.ui-btn-down-d a.ui-link-inherit { | |
border: 1px solid #808080; | |
background: #ced0d2; | |
font-weight: bold; | |
color: #111; | |
text-shadow: none; | |
background-image: -moz-linear-gradient(top, | |
#cccccc, | |
#eeeeee); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #cccccc), | |
color-stop(1, #eeeeee)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#cccccc', EndColorStr='#eeeeee')"; | |
} | |
.ui-btn-up-d, | |
.ui-btn-hover-d, | |
.ui-btn-down-d { | |
font-family: Helvetica, Arial, sans-serif; | |
text-decoration: none; | |
} | |
/* E | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-e { | |
border: 1px solid #F7C942; | |
background: #fadb4e; | |
color: #333; | |
text-shadow: 0 1px 0 #fff; | |
background-image: -moz-linear-gradient(top, | |
#fceda7, | |
#fadb4e); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fceda7), | |
color-stop(1, #fadb4e)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; | |
} | |
.ui-bar-e, | |
.ui-bar-e input, | |
.ui-bar-e select, | |
.ui-bar-e textarea, | |
.ui-bar-d button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-bar-e .ui-link-inherit { | |
color: #333; | |
} | |
.ui-bar-e .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-body-e { | |
border: 1px solid #F7C942; | |
color: #333333; | |
text-shadow: 0 1px 0 #fff; | |
background: #faeb9e; | |
background-image: -moz-linear-gradient(top, | |
#fff, | |
#faeb9e); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fff), | |
color-stop(1, #faeb9e)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#faeb9e')"; | |
} | |
.ui-body-e, | |
.ui-body-e input, | |
.ui-body-e select, | |
.ui-body-e textarea, | |
.ui-body-e button { | |
font-family: Helvetica, Arial, sans-serif; | |
} | |
.ui-body-e .ui-link-inherit { | |
color: #333333; | |
} | |
.ui-body-e .ui-link { | |
color: #2489CE; | |
font-weight: bold; | |
} | |
.ui-btn-up-e { | |
border: 1px solid #F7C942; | |
background: #fadb4e; | |
font-weight: bold; | |
color: #333; | |
text-shadow: 0 1px 1px #fe3; | |
text-shadow: 0 1px 0 #fff; | |
background-image: -moz-linear-gradient(top, | |
#fceda7, | |
#fadb4e); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fceda7), | |
color-stop(1, #fadb4e)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fceda7', EndColorStr='#fadb4e')"; | |
} | |
.ui-btn-up-e a.ui-link-inherit { | |
color: #333; | |
} | |
.ui-btn-hover-e { | |
border: 1px solid #e79952; | |
background: #fbe26f; | |
font-weight: bold; | |
color: #111; | |
text-shadow: 0 1px 1px #fff; | |
background-image: -moz-linear-gradient(top, | |
#fcf0b5, | |
#fbe26f); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fcf0b5), | |
color-stop(1, #fbe26f)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fcf0b5', EndColorStr='#fbe26f')"; | |
} | |
.ui-btn-hover-e a.ui-link-inherit { | |
color: #333; | |
} | |
.ui-btn-down-e { | |
border: 1px solid #F7C942; | |
background: #fceda7; | |
font-weight: bold; | |
color: #111; | |
text-shadow: 0 1px 1px #ffffff; | |
background-image: -moz-linear-gradient(top, | |
#fadb4e, | |
#fceda7); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #fadb4e), | |
color-stop(1, #fceda7)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#fadb4e', EndColorStr='#fceda7')"; | |
} | |
.ui-btn-down-e a.ui-link-inherit { | |
color: #333; | |
} | |
.ui-btn-up-e, | |
.ui-btn-hover-e, | |
.ui-btn-down-e { | |
font-family: Helvetica, Arial, sans-serif; | |
text-decoration: none; | |
} | |
/* links within "buttons" | |
-----------------------------------------------------------------------------------------------------------*/ | |
a.ui-link-inherit { | |
text-decoration: none !important; | |
} | |
/* Active class used as the "on" state across all themes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-active { | |
border: 1px solid #155678; | |
background: #4596ce; | |
font-weight: bold; | |
color: #fff; | |
cursor: pointer; | |
text-shadow: 0 -1px 1px #145072; | |
text-decoration: none; | |
background-image: -moz-linear-gradient(top, | |
#85bae4, | |
#5393c5); | |
background-image: -webkit-gradient(linear,left top,left bottom, | |
color-stop(0, #85bae4), | |
color-stop(1, #5393c5)); | |
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#85bae4', EndColorStr='#5393c5')"; | |
outline: none; | |
} | |
.ui-btn-active a.ui-link-inherit { | |
color: #fff; | |
} | |
/* button inner top highlight | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-inner { | |
border-top: 1px solid #fff; | |
border-color: rgba(255,255,255,.3); | |
} | |
/* corner rounding classes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-corner-tl { | |
-moz-border-radius-topleft: .6em; | |
-webkit-border-top-left-radius: .6em; | |
border-top-left-radius: .6em; | |
} | |
.ui-corner-tr { | |
-moz-border-radius-topright: .6em; | |
-webkit-border-top-right-radius: .6em; | |
border-top-right-radius: .6em; | |
} | |
.ui-corner-bl { | |
-moz-border-radius-bottomleft: .6em; | |
-webkit-border-bottom-left-radius: .6em; | |
border-bottom-left-radius: .6em; | |
} | |
.ui-corner-br { | |
-moz-border-radius-bottomright: .6em; | |
-webkit-border-bottom-right-radius: .6em; | |
border-bottom-right-radius: .6em; | |
} | |
.ui-corner-top { | |
-moz-border-radius-topleft: .6em; | |
-webkit-border-top-left-radius: .6em; | |
border-top-left-radius: .6em; | |
-moz-border-radius-topright: .6em; | |
-webkit-border-top-right-radius: .6em; | |
border-top-right-radius: .6em; | |
} | |
.ui-corner-bottom { | |
-moz-border-radius-bottomleft: .6em; | |
-webkit-border-bottom-left-radius: .6em; | |
border-bottom-left-radius: .6em; | |
-moz-border-radius-bottomright: .6em; | |
-webkit-border-bottom-right-radius: .6em; | |
border-bottom-right-radius: .6em; | |
} | |
.ui-corner-right { | |
-moz-border-radius-topright: .6em; | |
-webkit-border-top-right-radius: .6em; | |
border-top-right-radius: .6em; | |
-moz-border-radius-bottomright: .6em; | |
-webkit-border-bottom-right-radius: .6em; | |
border-bottom-right-radius: .6em; | |
} | |
.ui-corner-left { | |
-moz-border-radius-topleft: .6em; | |
-webkit-border-top-left-radius: .6em; | |
border-top-left-radius: .6em; | |
-moz-border-radius-bottomleft: .6em; | |
-webkit-border-bottom-left-radius: .6em; | |
border-bottom-left-radius: .6em; | |
} | |
.ui-corner-all { | |
-moz-border-radius: .6em; | |
-webkit-border-radius: .6em; | |
border-radius: .6em; | |
} | |
/* Interaction cues | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-disabled { | |
opacity: .3; | |
} | |
.ui-disabled, | |
.ui-disabled a { | |
cursor: default !important; | |
} | |
/* Icons | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-icon { | |
background: #666; | |
background: rgba(0,0,0,.4); | |
background-image: url(images/icons-18-white.png); | |
background-repeat: no-repeat; | |
-moz-border-radius: 9px; | |
-webkit-border-radius: 9px; | |
border-radius: 9px; | |
} | |
/* Alt icon color | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-icon-alt { | |
background: #fff; | |
background: rgba(255,255,255,.3); | |
background-image: url(images/icons-18-black.png); | |
background-repeat: no-repeat; | |
} | |
/* HD/"retina" sprite | |
-----------------------------------------------------------------------------------------------------------*/ | |
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), | |
only screen and (min--moz-device-pixel-ratio: 1.5), | |
only screen and (min-resolution: 240dpi) { | |
.ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r, | |
.ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check, | |
.ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back, | |
.ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search, | |
.ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on { | |
background-image: url(images/icons-36-white.png); | |
-moz-background-size: 776px 18px; | |
-o-background-size: 776px 18px; | |
-webkit-background-size: 776px 18px; | |
background-size: 776px 18px; | |
} | |
.ui-icon-alt { | |
background-image: url(images/icons-36-black.png); | |
} | |
} | |
/* plus minus */ | |
.ui-icon-plus { | |
background-position: -0 50%; | |
} | |
.ui-icon-minus { | |
background-position: -36px 50%; | |
} | |
/* delete/close */ | |
.ui-icon-delete { | |
background-position: -72px 50%; | |
} | |
/* arrows */ | |
.ui-icon-arrow-r { | |
background-position: -108px 50%; | |
} | |
.ui-icon-arrow-l { | |
background-position: -144px 50%; | |
} | |
.ui-icon-arrow-u { | |
background-position: -180px 50%; | |
} | |
.ui-icon-arrow-d { | |
background-position: -216px 50%; | |
} | |
/* misc */ | |
.ui-icon-check { | |
background-position: -252px 50%; | |
} | |
.ui-icon-gear { | |
background-position: -288px 50%; | |
} | |
.ui-icon-refresh { | |
background-position: -324px 50%; | |
} | |
.ui-icon-forward { | |
background-position: -360px 50%; | |
} | |
.ui-icon-back { | |
background-position: -396px 50%; | |
} | |
.ui-icon-grid { | |
background-position: -432px 50%; | |
} | |
.ui-icon-star { | |
background-position: -468px 50%; | |
} | |
.ui-icon-alert { | |
background-position: -504px 50%; | |
} | |
.ui-icon-info { | |
background-position: -540px 50%; | |
} | |
.ui-icon-home { | |
background-position: -576px 50%; | |
} | |
.ui-icon-search { | |
background-position: -612px 50%; | |
} | |
.ui-icon-checkbox-off { | |
background-position: -684px 50%; | |
} | |
.ui-icon-checkbox-on { | |
background-position: -648px 50%; | |
} | |
.ui-icon-radio-off { | |
background-position: -756px 50%; | |
} | |
.ui-icon-radio-on { | |
background-position: -720px 50%; | |
} | |
/* checks,radios */ | |
.ui-icon-checkbox-off, | |
.ui-icon-checkbox-on, | |
.ui-icon-radio-off, | |
.ui-icon-radio-on { | |
background-color: transparent; | |
-moz-border-radius: 0; | |
-webkit-border-radius: 0; | |
border-radius: 0; | |
} | |
.ui-icon-searchfield { | |
background-image: url(images/icon-search-black.png); | |
background-size: 16px 16px; | |
} | |
/* loading icon */ | |
.ui-icon-loading { | |
background-image: url(images/ajax-loader.png); | |
width: 40px; | |
height: 40px; | |
-moz-border-radius: 20px; | |
-webkit-border-radius: 20px; | |
border-radius: 20px; | |
background-size: 35px 35px; | |
} | |
/* Button corner classes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-corner-tl { | |
-moz-border-radius-topleft: 1em; | |
-webkit-border-top-left-radius: 1em; | |
border-top-left-radius: 1em; | |
} | |
.ui-btn-corner-tr { | |
-moz-border-radius-topright: 1em; | |
-webkit-border-top-right-radius: 1em; | |
border-top-right-radius: 1em; | |
} | |
.ui-btn-corner-bl { | |
-moz-border-radius-bottomleft: 1em; | |
-webkit-border-bottom-left-radius: 1em; | |
border-bottom-left-radius: 1em; | |
} | |
.ui-btn-corner-br { | |
-moz-border-radius-bottomright: 1em; | |
-webkit-border-bottom-right-radius: 1em; | |
border-bottom-right-radius: 1em; | |
} | |
.ui-btn-corner-top { | |
-moz-border-radius-topleft: 1em; | |
-webkit-border-top-left-radius: 1em; | |
border-top-left-radius: 1em; | |
-moz-border-radius-topright: 1em; | |
-webkit-border-top-right-radius: 1em; | |
border-top-right-radius: 1em; | |
} | |
.ui-btn-corner-bottom { | |
-moz-border-radius-bottomleft: 1em; | |
-webkit-border-bottom-left-radius: 1em; | |
border-bottom-left-radius: 1em; | |
-moz-border-radius-bottomright: 1em; | |
-webkit-border-bottom-right-radius: 1em; | |
border-bottom-right-radius: 1em; | |
} | |
.ui-btn-corner-right { | |
-moz-border-radius-topright: 1em; | |
-webkit-border-top-right-radius: 1em; | |
border-top-right-radius: 1em; | |
-moz-border-radius-bottomright: 1em; | |
-webkit-border-bottom-right-radius: 1em; | |
border-bottom-right-radius: 1em; | |
} | |
.ui-btn-corner-left { | |
-moz-border-radius-topleft: 1em; | |
-webkit-border-top-left-radius: 1em; | |
border-top-left-radius: 1em; | |
-moz-border-radius-bottomleft: 1em; | |
-webkit-border-bottom-left-radius: 1em; | |
border-bottom-left-radius: 1em; | |
} | |
.ui-btn-corner-all { | |
-moz-border-radius: 1em; | |
-webkit-border-radius: 1em; | |
border-radius: 1em; | |
} | |
/* radius clip workaround for cleaning up corner trapping */ | |
.ui-corner-tl, | |
.ui-corner-tr, | |
.ui-corner-bl, | |
.ui-corner-br, | |
.ui-corner-top, | |
.ui-corner-bottom, | |
.ui-corner-right, | |
.ui-corner-left, | |
.ui-corner-all, | |
.ui-btn-corner-tl, | |
.ui-btn-corner-tr, | |
.ui-btn-corner-bl, | |
.ui-btn-corner-br, | |
.ui-btn-corner-top, | |
.ui-btn-corner-bottom, | |
.ui-btn-corner-right, | |
.ui-btn-corner-left, | |
.ui-btn-corner-all { | |
-webkit-background-clip: padding-box; | |
-moz-background-clip: padding-box; | |
background-clip: padding-box; | |
} | |
/* Overlay / modal | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-overlay { | |
background: #666; | |
opacity: .5; | |
filter: Alpha(Opacity=50); | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
} | |
.ui-overlay-shadow { | |
-moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
-webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
} | |
.ui-shadow { | |
-moz-box-shadow: 0px 1px 4px rgba(0,0,0,.3); | |
-webkit-box-shadow: 0px 1px 4px rgba(0,0,0,.3); | |
box-shadow: 0px 1px 4px rgba(0,0,0,.3); | |
} | |
.ui-bar-a .ui-shadow, | |
.ui-bar-b .ui-shadow , | |
.ui-bar-c .ui-shadow { | |
-moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
-webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
} | |
.ui-shadow-inset { | |
-moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
-webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
} | |
.ui-icon-shadow { | |
-moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
-webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
} | |
/* Focus state - set here for specificity | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-focus { | |
-moz-box-shadow: 0px 0px 12px #387bbe; | |
-webkit-box-shadow: 0px 0px 12px #387bbe; | |
box-shadow: 0px 0px 12px #387bbe; | |
} | |
/* unset box shadow in browsers that don't do it right | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-mobile-nosupport-boxshadow * { | |
-moz-box-shadow: none !important; | |
-webkit-box-shadow: none !important; | |
box-shadow: none !important; | |
} | |
/* ...and bring back focus */ | |
.ui-mobile-nosupport-boxshadow .ui-focus { | |
outline-width: 2px; | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. | |
* Note: Code is in draft form and is subject to change | |
*/ | |
/* some unsets - more probably needed */ | |
.ui-mobile, .ui-mobile body { height: 100%; } | |
.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } | |
.ui-mobile a img, .ui-mobile fieldset { border: 0; } | |
/* responsive page widths */ | |
.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } | |
/* "page" containers - full-screen views, one should always be in view post-pageload */ | |
.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } | |
.ui-mobile .ui-page-active { display: block; overflow: visible; } | |
/*orientations from js are available */ | |
.portrait, | |
.portrait .ui-page { min-height: 100%; } | |
.landscape, | |
.landscape .ui-page { min-height: 100%; } | |
/* loading screen */ | |
.ui-loading .ui-mobile-viewport { overflow: hidden !important; } | |
.ui-loading .ui-loader { display: block; } | |
.ui-loading .ui-page { overflow: hidden; } | |
.ui-loader { display: none; position: absolute; opacity: .85; z-index: 10; left: 50%; width: 200px; margin-left: -130px; margin-top: -35px; padding: 10px 30px; } | |
.ui-loader h1 { font-size: 15px; text-align: center; } | |
.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; } | |
/*fouc*/ | |
.ui-mobile-rendering > * { visibility: hidden; } | |
/*headers, content panels*/ | |
.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; } | |
.ui-bar { font-size: 16px; margin: 0; } | |
.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; } | |
.ui-header, .ui-footer { display: block; } | |
.ui-page .ui-header, .ui-page .ui-footer { position: relative; } | |
.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; } | |
.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; } | |
.ui-header .ui-title, .ui-footer .ui-title { text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } | |
/*content area*/ | |
.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; } | |
.ui-page-fullscreen .ui-content { padding:0; } | |
/* icons sizing */ | |
.ui-icon { width: 18px; height: 18px; } | |
/* fullscreen class on ui-content div */ | |
.ui-fullscreen { } | |
.ui-fullscreen img { max-width: 100%; } | |
/* non-js content hiding */ | |
.ui-nojs { position: absolute; left: -9999px; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.spin { | |
-webkit-transform: rotate(360deg); | |
-webkit-animation-name: spin; | |
-webkit-animation-duration: 1s; | |
-webkit-animation-iteration-count: infinite; | |
} | |
@-webkit-keyframes spin { | |
from {-webkit-transform: rotate(0deg);} | |
to {-webkit-transform: rotate(360deg);} | |
} | |
/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/ | |
Built by David Kaneda and maintained by Jonathan Stark. | |
*/ | |
.in, .out { | |
-webkit-animation-timing-function: ease-in-out; | |
-webkit-animation-duration: 350ms; | |
} | |
.slide.in { | |
-webkit-transform: translateX(0); | |
-webkit-animation-name: slideinfromright; | |
} | |
.slide.out { | |
-webkit-transform: translateX(-100%); | |
-webkit-animation-name: slideouttoleft; | |
} | |
.slide.in.reverse { | |
-webkit-transform: translateX(0); | |
-webkit-animation-name: slideinfromleft; | |
} | |
.slide.out.reverse { | |
-webkit-transform: translateX(100%); | |
-webkit-animation-name: slideouttoright; | |
} | |
.slideup.in { | |
-webkit-transform: translateY(0); | |
-webkit-animation-name: slideinfrombottom; | |
z-index: 10; | |
} | |
.slideup.out { | |
-webkit-animation-name: dontmove; | |
z-index: 0; | |
} | |
.slideup.out.reverse { | |
-webkit-transform: translateY(100%); | |
z-index: 10; | |
-webkit-animation-name: slideouttobottom; | |
} | |
.slideup.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
.slidedown.in { | |
-webkit-transform: translateY(0); | |
-webkit-animation-name: slideinfromtop; | |
z-index: 10; | |
} | |
.slidedown.out { | |
-webkit-animation-name: dontmove; | |
z-index: 0; | |
} | |
.slidedown.out.reverse { | |
-webkit-transform: translateY(-100%); | |
z-index: 10; | |
-webkit-animation-name: slideouttotop; | |
} | |
.slidedown.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
@-webkit-keyframes slideinfromright { | |
from { -webkit-transform: translateX(100%); } | |
to { -webkit-transform: translateX(0); } | |
} | |
@-webkit-keyframes slideinfromleft { | |
from { -webkit-transform: translateX(-100%); } | |
to { -webkit-transform: translateX(0); } | |
} | |
@-webkit-keyframes slideouttoleft { | |
from { -webkit-transform: translateX(0); } | |
to { -webkit-transform: translateX(-100%); } | |
} | |
@-webkit-keyframes slideouttoright { | |
from { -webkit-transform: translateX(0); } | |
to { -webkit-transform: translateX(100%); } | |
} | |
@-webkit-keyframes slideinfromtop { | |
from { -webkit-transform: translateY(-100%); } | |
to { -webkit-transform: translateY(0); } | |
} | |
@-webkit-keyframes slideinfrombottom { | |
from { -webkit-transform: translateY(100%); } | |
to { -webkit-transform: translateY(0); } | |
} | |
@-webkit-keyframes slideouttobottom { | |
from { -webkit-transform: translateY(0); } | |
to { -webkit-transform: translateY(100%); } | |
} | |
@-webkit-keyframes slideouttotop { | |
from { -webkit-transform: translateY(0); } | |
to { -webkit-transform: translateY(-100%); } | |
} | |
@-webkit-keyframes fadein { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
@-webkit-keyframes fadeout { | |
from { opacity: 1; } | |
to { opacity: 0; } | |
} | |
.fade.in { | |
opacity: 1; | |
z-index: 10; | |
-webkit-animation-name: fadein; | |
} | |
.fade.out { | |
z-index: 0; | |
-webkit-animation-name: fadeout; | |
} | |
/* The properties in this body rule are only necessary for the 'flip' transition. | |
* We need specify the perspective to create a projection matrix. This will add | |
* some depth as the element flips. The depth number represents the distance of | |
* the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate | |
* value. | |
*/ | |
.ui-mobile-viewport-perspective { | |
-webkit-perspective: 1000; | |
position: absolute; | |
} | |
.ui-mobile-viewport-transitioning, | |
.ui-mobile-viewport-transitioning .ui-page { | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
} | |
.flip { | |
-webkit-animation-duration: .65s; | |
-webkit-backface-visibility:hidden; | |
-webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ | |
} | |
.flip.in { | |
-webkit-transform: rotateY(0) scale(1); | |
-webkit-animation-name: flipinfromleft; | |
} | |
.flip.out { | |
-webkit-transform: rotateY(-180deg) scale(.8); | |
-webkit-animation-name: flipouttoleft; | |
} | |
/* Shake it all about */ | |
.flip.in.reverse { | |
-webkit-transform: rotateY(0) scale(1); | |
-webkit-animation-name: flipinfromright; | |
} | |
.flip.out.reverse { | |
-webkit-transform: rotateY(180deg) scale(.8); | |
-webkit-animation-name: flipouttoright; | |
} | |
@-webkit-keyframes flipinfromright { | |
from { -webkit-transform: rotateY(-180deg) scale(.8); } | |
to { -webkit-transform: rotateY(0) scale(1); } | |
} | |
@-webkit-keyframes flipinfromleft { | |
from { -webkit-transform: rotateY(180deg) scale(.8); } | |
to { -webkit-transform: rotateY(0) scale(1); } | |
} | |
@-webkit-keyframes flipouttoleft { | |
from { -webkit-transform: rotateY(0) scale(1); } | |
to { -webkit-transform: rotateY(-180deg) scale(.8); } | |
} | |
@-webkit-keyframes flipouttoright { | |
from { -webkit-transform: rotateY(0) scale(1); } | |
to { -webkit-transform: rotateY(180deg) scale(.8); } | |
} | |
/* Hackish, but reliable. */ | |
@-webkit-keyframes dontmove { | |
from { opacity: 1; } | |
to { opacity: 1; } | |
} | |
.pop { | |
-webkit-transform-origin: 50% 50%; | |
} | |
.pop.in { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
-webkit-animation-name: popin; | |
z-index: 10; | |
} | |
.pop.out.reverse { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
-webkit-animation-name: popout; | |
z-index: 10; | |
} | |
.pop.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
@-webkit-keyframes popin { | |
from { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
} | |
to { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
} | |
} | |
@-webkit-keyframes popout { | |
from { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
} | |
to { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
} | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* content configurations. */ | |
.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; } | |
.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height:1px;} | |
/* grid solo: 100 - single item fallback */ | |
.ui-grid-solo .ui-block-a { width: 100%; float: none; } | |
/* grid a: 50/50 */ | |
.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; } | |
.ui-grid-a .ui-block-a { clear: left; } | |
/* grid b: 33/33/33 */ | |
.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; } | |
.ui-grid-b .ui-block-a { clear: left; } | |
/* grid c: 25/25/25/25 */ | |
.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; } | |
.ui-grid-c .ui-block-a { clear: left; } | |
/* grid d: 20/20/20/20/20 */ | |
.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; } | |
.ui-grid-d .ui-block-a { clear: left; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* fixed page header & footer configuration */ | |
.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; } | |
.ui-header-fixed, .ui-footer-fixed { | |
z-index: 1000; | |
-webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */ | |
} | |
.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; } | |
.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-navbar { overflow: hidden; } | |
.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;} | |
.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; } | |
.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; } | |
.ui-navbar li.ui-navbar-truncate { position: absolute; left: -9999px; top: -9999px; } | |
.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; border-right-width: 0; } | |
.ui-navbar li .ui-btn { margin-right: -1px; } | |
.ui-navbar li .ui-btn:last-child { margin-right: 0; } | |
.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn, | |
.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; } | |
.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; } | |
.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; } | |
/*expanded page styles*/ | |
.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; } | |
.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; } | |
.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; } | |
.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; } | |
.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner { padding: 15px 5px 45px; text-align: center; } | |
.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon { bottom: 15px; } | |
.ui-navbar-expanded li .ui-btn .ui-btn-inner { min-height: 2.5em; } | |
.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner { padding-top: 1.8em; padding-bottom: 1.9em; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 5px; padding: 0; } | |
.ui-btn:focus, .ui-btn:active { outline: none; } | |
.ui-header .ui-btn, .ui-footer .ui-btn, .ui-bar .ui-btn { display: inline-block; font-size: 13px; margin: 0; } | |
.ui-btn-inline { display: inline-block; } | |
.ui-btn-inner { padding: .6em 25px; display: block; height: 100%; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; position: relative; } | |
.ui-header .ui-btn-inner, .ui-footer .ui-btn-inner, .ui-bar .ui-btn-inner { padding: .4em 8px .5em; } | |
.ui-btn-icon-notext { display: inline-block; width: 20px; height: 20px; padding: 2px 1px 2px 3px; text-indent: -9999px; } | |
.ui-btn-icon-notext .ui-btn-inner { padding: 0; } | |
.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -999px; } | |
.ui-btn-icon-left .ui-btn-inner { padding-left: 33px; } | |
.ui-header .ui-btn-icon-left .ui-btn-inner, | |
.ui-footer .ui-btn-icon-left .ui-btn-inner, | |
.ui-bar .ui-btn-icon-left .ui-btn-inner { padding-left: 27px; } | |
.ui-btn-icon-right .ui-btn-inner { padding-right: 33px; } | |
.ui-header .ui-btn-icon-right .ui-btn-inner, | |
.ui-footer .ui-btn-icon-right .ui-btn-inner, | |
.ui-bar .ui-btn-icon-right .ui-btn-inner { padding-right: 27px; } | |
.ui-btn-icon-top .ui-btn-inner { padding-top: 33px; } | |
.ui-header .ui-btn-icon-top .ui-btn-inner, | |
.ui-footer .ui-btn-icon-top .ui-btn-inner, | |
.ui-bar .ui-btn-icon-top .ui-btn-inner { padding-top: 27px; } | |
.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 33px; } | |
.ui-header .ui-btn-icon-bottom .ui-btn-inner, | |
.ui-footer .ui-btn-icon-bottom .ui-btn-inner, | |
.ui-bar .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 27px; } | |
/*btn icon positioning*/ | |
.ui-btn-icon-notext .ui-icon { display: block; } | |
.ui-btn-icon-left .ui-icon, .ui-btn-icon-right .ui-icon { position: absolute; top: 50%; margin-top: -9px; } | |
.ui-btn-icon-top .ui-icon, .ui-btn-icon-bottom .ui-icon { position: absolute; left: 50%; margin-left: -9px; } | |
.ui-btn-icon-left .ui-icon { left: 10px; } | |
.ui-btn-icon-right .ui-icon {right: 10px; } | |
.ui-header .ui-btn-icon-left .ui-icon, | |
.ui-footer .ui-btn-icon-left .ui-icon, | |
.ui-bar .ui-btn-icon-left .ui-icon { left: 4px; } | |
.ui-header .ui-btn-icon-right .ui-icon, | |
.ui-footer .ui-btn-icon-right .ui-icon, | |
.ui-bar .ui-btn-icon-right .ui-icon { right: 4px; } | |
.ui-header .ui-btn-icon-top .ui-icon, | |
.ui-footer .ui-btn-icon-top .ui-icon, | |
.ui-bar .ui-btn-icon-top .ui-icon { top: 4px; } | |
.ui-header .ui-btn-icon-bottom .ui-icon, | |
.ui-footer .ui-btn-icon-bottom .ui-icon, | |
.ui-bar .ui-btn-icon-bottom .ui-icon { bottom: 4px; } | |
.ui-btn-icon-top .ui-icon { top: 5px; } | |
.ui-btn-icon-bottom .ui-icon { bottom: 5px; } | |
/*hiding native button,inputs */ | |
.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: button; opacity: 0; cursor: pointer; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-collapsible-contain { margin: .5em 0; } | |
.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; } | |
.ui-collapsible-heading a { text-align: left; margin: 0; } | |
.ui-collapsible-heading a .ui-btn-inner { padding-left: 40px; } | |
.ui-collapsible-heading a span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; } | |
.ui-collapsible-heading a span.ui-btn .ui-btn-inner { padding: 0; } | |
.ui-collapsible-heading a span.ui-btn .ui-icon { left: 0; margin-top: -10px; } | |
.ui-collapsible-heading-status { position:absolute; left:-9999px; } | |
.ui-collapsible-content { display: block; padding: 10px 0 10px 8px; } | |
.ui-collapsible-content-collapsed { display: none; } | |
.ui-collapsible-set { margin: .5em 0; } | |
.ui-collapsible-set .ui-collapsible-contain { margin: -1px 0 0; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0 1em; } | |
.ui-bar .ui-controlgroup { margin: 0 .3em; } | |
.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; } | |
.ui-controlgroup-controls { display: block; width: 95%;} | |
.ui-controlgroup li { list-style: none; } | |
.ui-controlgroup-vertical .ui-btn, | |
.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; } | |
.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; } | |
.ui-controlgroup-horizontal { padding: 0; } | |
.ui-controlgroup-horizontal .ui-btn, | |
.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline-block; margin: 0 -5px 0 0; } | |
.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { display: inline; } | |
.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn, | |
.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; } | |
.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; } | |
.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; } | |
/* conflicts with listview.. | |
.ui-controlgroup .ui-btn-icon-notext { width: 30px; height: 30px; text-indent: -9999px; } | |
.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner { padding: 5px 6px 5px 5px; } | |
*/ | |
.min-width-480px .ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.min-width-480px .ui-controlgroup-controls { width: 60%; display: inline-block; } /* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-dialog { min-height: 480px; } | |
.ui-dialog .ui-header, .ui-dialog .ui-content, .ui-dialog .ui-footer { margin: 15px; position: relative; } | |
.ui-dialog .ui-header, .ui-dialog .ui-footer { z-index: 10; width: auto; } | |
.ui-dialog .ui-content, .ui-dialog .ui-footer { margin-top: -15px; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-checkbox, .ui-radio { position:relative; margin: .2em 0 .5em; z-index: 1; } | |
.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; z-index: 2; } | |
.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; } | |
.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } | |
.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon {left: 15px; } | |
.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon {right: 15px; } | |
/* input, label positioning */ | |
.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-field-contain { background: none; padding: 1.5em 0; margin: 0; border-bottom-width: 1px; overflow: visible; } | |
.ui-field-contain:first-child { border-top-width: 0; } | |
.min-width-480px .ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-select { display: block; position: relative; } | |
.ui-select select { position: absolute; left: -9999px; top: -9999px; } | |
.ui-select .ui-btn { overflow: hidden; } | |
.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: button; left: 0; top:0; width: 100%; height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); } | |
.ui-select .ui-btn select.ui-select-nativeonly { opacity: 1; } | |
.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } | |
.ui-select .ui-btn-icon-right .ui-icon { right: 15px; } | |
/* labels */ | |
label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; } | |
/*listbox*/ | |
.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: inline-block; min-height: 1em; } | |
.ui-select .ui-btn-text { text-overflow: ellipsis; overflow: hidden; display: block;} | |
.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; } | |
.ui-selectmenu .ui-listview { margin: 0; } | |
.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } | |
.ui-selectmenu-hidden { top: -9999px; left: -9999px; } | |
.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; } | |
.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; } | |
.ui-selectmenu-list .ui-li .ui-icon { display: block; } | |
.ui-li.ui-selectmenu-placeholder { display: none; } | |
.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; } | |
.min-width-480px label.ui-select { display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.min-width-480px .ui-select { width: 60%; display: inline-block; } | |
/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ | |
.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; } | |
input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; } | |
input.ui-input-text { -webkit-appearance: none; } | |
textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; } | |
.ui-input-search { padding: 0 30px; width: 77%; background-position: 8px 50%; background-repeat: no-repeat; position: relative; } | |
.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; } | |
.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -14px; } | |
.ui-input-search .ui-input-clear-hidden { display: none; } | |
/* orientation adjustments - incomplete!*/ | |
.min-width-480px label.ui-input-text { vertical-align: top; } | |
.min-width-480px label.ui-input-text { display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.min-width-480px input.ui-input-text, | |
.min-width-480px textarea.ui-input-text, | |
.min-width-480px .ui-input-search { width: 60%; display: inline-block; } | |
.min-width-480px .ui-input-search { width: 50%; } | |
.min-width-480px .ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-listview { margin: 0; counter-reset: listnumbering; } | |
.ui-content .ui-listview { margin: -15px; } | |
.ui-content .ui-listview-inset { margin: 1em 0; } | |
.ui-listview, .ui-li { list-style:none; padding:0; } | |
.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; } | |
.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; } | |
.ui-li-divider { counter-reset: listnumbering; } | |
ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; } | |
ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */ | |
.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; } | |
.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; } | |
.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; } | |
.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 75px .7em 15px; display: block; } | |
.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb { min-height: 60px; padding-left: 100px; } | |
.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon { min-height: 20px; padding-left: 40px; } | |
.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; } | |
.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; } | |
.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; } | |
.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; } | |
.min-width-480px .ui-li-aside { width: 45%; } | |
.ui-li-divider { cursor: default; } | |
.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 95px; } | |
.ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; } | |
.ui-li-divider .ui-li-count, .ui-li-static .ui-li-count { right: 10px; } | |
.ui-li-has-alt .ui-li-count { right: 55px; } | |
.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; } | |
.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; } | |
.ui-li-link-alt .ui-btn-inner { padding: 0; position: static; } | |
.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; } | |
.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px } | |
.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; } | |
.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; } | |
/* Odd iPad positioning issue. */ | |
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { | |
.ui-li .ui-btn-text { overflow: visible; } | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
label.ui-slider { display: block; } | |
input.ui-slider-input, .min-width-480px input.ui-slider-input { display: inline-block; width: 50px; } | |
select.ui-slider-switch { display: none; } | |
div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 66%; } | |
a.ui-slider-handle { position: absolute; z-index: 10; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; } | |
a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; } | |
.min-width-480px label.ui-slider { display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.min-width-480px div.ui-slider { width: 45%; } | |
div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; } | |
div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; } | |
div.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; } | |
div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; } | |
div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; } | |
div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; } | |
.ui-slider-switch-a div.ui-slider-labelbg-a, .ui-slider-switch-b div.ui-slider-labelbg-b { z-index: -1; } | |
.ui-slider-switch-a div.ui-slider-labelbg-b, .ui-slider-switch-b div.ui-slider-labelbg-a { z-index: 0; } | |
div.ui-slider-switch a.ui-slider-handle { z-index: 20; width: 101%; height: 32px; margin-top: -18px; margin-left: -101%; } | |
span.ui-slider-label { width: 100%; position: absolute;height: 32px; font-size: 16px; text-align: center; line-height: 2; background: none; border-color: transparent; } | |
span.ui-slider-label-a { left: -100%; margin-right: -1px } | |
span.ui-slider-label-b { right: -100%; margin-left: -1px } | |
/*! | |
* jQuery Mobile v1.0rc1 | |
* http://jquerymobile.com/ | |
* | |
* Copyright 2010, jQuery Project | |
* Dual licensed under the MIT or GPL Version 2 licenses. | |
* http://jquery.org/license | |
*/ | |
/*! | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* Swatches */ | |
/* A | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-a { | |
border: 1px solid #2A2A2A /*{a-bar-border}*/; | |
background: #111111 /*{a-bar-background-color}*/; | |
color: #ffffff /*{a-bar-color}*/; | |
font-weight: bold; | |
text-shadow: 0 /*{a-bar-shadow-x}*/ -1px /*{a-bar-shadow-y}*/ 1px /*{a-bar-shadow-radius}*/ #000000 /*{a-bar-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c /*{a-bar-background-start}*/), to(#111 /*{a-bar-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #3c3c3c /*{a-bar-background-start}*/, #111 /*{a-bar-background-end}*/); | |
} | |
.ui-bar-a, | |
.ui-bar-a input, | |
.ui-bar-a select, | |
.ui-bar-a textarea, | |
.ui-bar-a button { | |
font-family: Helvetica, Arial, sans-serif /*{a-bar-font}*/; | |
} | |
.ui-bar-a .ui-link-inherit { | |
color: #fff /*{a-bar-color}*/; | |
} | |
.ui-bar-a .ui-link { | |
color: #7cc4e7 /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-body-a { | |
border: 1px solid #2A2A2A /*{a-body-border}*/; | |
background: #222222 /*{a-body-background-color}*/; | |
color: #fff /*{a-body-color}*/; | |
text-shadow: 0 /*{a-body-shadow-x}*/ 1px /*{a-body-shadow-y}*/ 0 /*{a-body-shadow-radius}*/ #000 /*{a-body-shadow-color}*/; | |
font-weight: normal; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#666 /*{a-body-background-start}*/), to(#222 /*{a-body-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #666 /*{a-body-background-start}*/, #222 /*{a-body-background-end}*/); | |
} | |
.ui-body-a, | |
.ui-body-a input, | |
.ui-body-a select, | |
.ui-body-a textarea, | |
.ui-body-a button { | |
font-family: Helvetica, Arial, sans-serif /*{a-body-font}*/; | |
} | |
.ui-body-a .ui-link-inherit { | |
color: #fff /*{a-body-color}*/; | |
} | |
.ui-body-a .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-br { | |
border-bottom: rgb(130,130,130); | |
border-bottom: rgba(130,130,130,.3); | |
border-bottom-width: 1px; | |
border-bottom-style: solid; | |
} | |
.ui-btn-up-a { | |
border: 1px solid #222 /*{a-bup-border}*/; | |
background: #333333 /*{a-bup-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{a-bup-color}*/; | |
text-shadow: 0 /*{a-bup-shadow-x}*/ -1px /*{a-bup-shadow-y}*/ 1px /*{a-bup-shadow-radius}*/ #000 /*{a-bup-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#555 /*{a-bup-background-start}*/), to(#333 /*{a-bup-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #555 /*{a-bup-background-start}*/, #333 /*{a-bup-background-end}*/); | |
} | |
.ui-btn-up-a a.ui-link-inherit { | |
color: #fff /*{a-bup-color}*/; | |
} | |
.ui-btn-hover-a { | |
border: 1px solid #000 /*{a-bhover-border}*/; | |
background: #444444 /*{a-bhover-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{a-bhover-color}*/; | |
text-shadow: 0 /*{a-bhover-shadow-x}*/ -1px /*{a-bhover-shadow-y}*/ 1px /*{a-bhover-shadow-radius}*/ #000 /*{a-bhover-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#666 /*{a-bhover-background-start}*/), to(#444 /*{a-bhover-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #666 /*{a-bhover-background-start}*/, #444 /*{a-bhover-background-end}*/); | |
} | |
.ui-btn-hover-a a.ui-link-inherit { | |
color: #fff /*{a-bhover-color}*/; | |
} | |
.ui-btn-down-a { | |
border: 1px solid #000 /*{a-bdown-border}*/; | |
background: #3d3d3d /*{a-bdown-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{a-bdown-color}*/; | |
text-shadow: 0 /*{a-bdown-shadow-x}*/ -1px /*{a-bdown-shadow-y}*/ 1px /*{a-bdown-shadow-radius}*/ #000 /*{a-bdown-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#333 /*{a-bdown-background-start}*/), to(#5a5a5a /*{a-bdown-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #333 /*{a-bdown-background-start}*/, #5a5a5a /*{a-bdown-background-end}*/); | |
} | |
.ui-btn-down-a a.ui-link-inherit { | |
color: #fff /*{a-bdown-color}*/; | |
} | |
.ui-btn-up-a, | |
.ui-btn-hover-a, | |
.ui-btn-down-a { | |
font-family: Helvetica, Arial, sans-serif /*{a-button-font}*/; | |
text-decoration: none; | |
} | |
/* B | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-b { | |
border: 1px solid #456f9a /*{b-bar-border}*/; | |
background: #5e87b0 /*{b-bar-background-color}*/; | |
color: #fff /*{b-bar-color}*/; | |
font-weight: bold; | |
text-shadow: 0 /*{b-bar-shadow-x}*/ -1px /*{b-bar-shadow-y}*/ 1px /*{b-bar-shadow-radius}*/ #254f7a /*{b-bar-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#81a8ce /*{b-bar-background-start}*/), to(#5e87b0 /*{b-bar-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #81a8ce /*{b-bar-background-start}*/, #5e87b0 /*{b-bar-background-end}*/); | |
} | |
.ui-bar-b, | |
.ui-bar-b input, | |
.ui-bar-b select, | |
.ui-bar-b textarea, | |
.ui-bar-b button { | |
font-family: Helvetica, Arial, sans-serif /*{b-bar-font}*/; | |
} | |
.ui-bar-b .ui-link-inherit { | |
color: #fff /*{b-bar-color}*/; | |
} | |
.ui-bar-b .ui-link { | |
color: #7cc4e7 /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-body-b { | |
border: 1px solid #C6C6C6 /*{b-body-border}*/; | |
background: #cccccc /*{b-body-background-color}*/; | |
color: #333333 /*{b-body-color}*/; | |
text-shadow: 0 /*{b-body-shadow-x}*/ 1px /*{b-body-shadow-y}*/ 0 /*{b-body-shadow-radius}*/ #fff /*{b-body-shadow-color}*/; | |
font-weight: normal; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#e6e6e6 /*{b-body-background-start}*/), to(#ccc /*{b-body-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #e6e6e6 /*{b-body-background-start}*/, #ccc /*{b-body-background-end}*/); | |
} | |
.ui-body-b, | |
.ui-body-b input, | |
.ui-body-b select, | |
.ui-body-b textarea, | |
.ui-body-b button { | |
font-family: Helvetica, Arial, sans-serif /*{b-body-font}*/; | |
} | |
.ui-body-b .ui-link-inherit { | |
color: #333333 /*{b-body-color}*/; | |
} | |
.ui-body-b .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-btn-up-b { | |
border: 1px solid #145072 /*{b-bup-border}*/; | |
background: #2567ab /*{b-bup-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{b-bup-color}*/; | |
text-shadow: 0 /*{b-bup-shadow-x}*/ -1px /*{b-bup-shadow-y}*/ 1px /*{b-bup-shadow-radius}*/ #145072 /*{b-bup-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#5f9cc5 /*{b-bup-background-start}*/), to(#396b9e /*{b-bup-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #5f9cc5 /*{b-bup-background-start}*/, #396b9e /*{b-bup-background-end}*/); | |
} | |
.ui-btn-up-b a.ui-link-inherit { | |
color: #fff /*{b-bup-color}*/; | |
} | |
.ui-btn-hover-b { | |
border: 1px solid #00516e /*{b-bhover-border}*/; | |
background: #4b88b6 /*{b-bhover-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{b-bhover-color}*/; | |
text-shadow: 0 /*{b-bhover-shadow-x}*/ -1px /*{b-bhover-shadow-y}*/ 1px /*{b-bhover-shadow-radius}*/ #014D68 /*{b-bhover-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#72b0d4 /*{b-bhover-background-start}*/), to(#4b88b6 /*{b-bhover-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #72b0d4 /*{b-bhover-background-start}*/, #4b88b6 /*{b-bhover-background-end}*/); | |
} | |
.ui-btn-hover-b a.ui-link-inherit { | |
color: #fff /*{b-bhover-color}*/; | |
} | |
.ui-btn-down-b { | |
border: 1px solid #225377 /*{b-bdown-border}*/; | |
background: #4e89c5 /*{b-bdown-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{b-bdown-color}*/; | |
text-shadow: 0 /*{b-bdown-shadow-x}*/ -1px /*{b-bdown-shadow-y}*/ 1px /*{b-bdown-shadow-radius}*/ #225377 /*{b-bdown-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#396b9e /*{b-bdown-background-start}*/), to(#4e89c5 /*{b-bdown-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #396b9e /*{b-bdown-background-start}*/, #4e89c5 /*{b-bdown-background-end}*/); | |
} | |
.ui-btn-down-b a.ui-link-inherit { | |
color: #fff /*{b-bdown-color}*/; | |
} | |
.ui-btn-up-b, | |
.ui-btn-hover-b, | |
.ui-btn-down-b { | |
font-family: Helvetica, Arial, sans-serif /*{b-button-font}*/; | |
text-decoration: none; | |
} | |
/* C | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-c { | |
border: 1px solid #B3B3B3 /*{c-bar-border}*/; | |
background: #e9eaeb /*{c-bar-background-color}*/; | |
color: #3E3E3E /*{c-bar-color}*/; | |
font-weight: bold; | |
text-shadow: 0 /*{c-bar-shadow-x}*/ 1px /*{c-bar-shadow-y}*/ 1px /*{c-bar-shadow-radius}*/ #fff /*{c-bar-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0f0 /*{c-bar-background-start}*/), to(#e9eaeb /*{c-bar-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #f0f0f0 /*{c-bar-background-start}*/, #e9eaeb /*{c-bar-background-end}*/); | |
} | |
.ui-bar-c .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-bar-c, | |
.ui-bar-c input, | |
.ui-bar-c select, | |
.ui-bar-c textarea, | |
.ui-bar-c button { | |
font-family: Helvetica, Arial, sans-serif /*{c-bar-font}*/; | |
} | |
.ui-body-c { | |
border: 1px solid #B3B3B3 /*{c-body-border}*/; | |
color: #333333 /*{c-body-color}*/; | |
text-shadow: 0 /*{c-body-shadow-x}*/ 1px /*{c-body-shadow-y}*/ 0 /*{c-body-shadow-radius}*/ #fff /*{c-body-shadow-color}*/; | |
background: #f0f0f0 /*{c-body-background-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{c-body-background-start}*/), to(#ddd /*{c-body-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #eee /*{c-body-background-start}*/, #ddd /*{c-body-background-end}*/); | |
} | |
.ui-body-c, | |
.ui-body-c input, | |
.ui-body-c select, | |
.ui-body-c textarea, | |
.ui-body-c button { | |
font-family: Helvetica, Arial, sans-serif /*{c-body-font}*/; | |
} | |
.ui-body-c .ui-link-inherit { | |
color: #333333 /*{c-body-color}*/; | |
} | |
.ui-body-c .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-btn-up-c { | |
border: 1px solid #ccc /*{c-bup-border}*/; | |
background: #eee /*{c-bup-background-color}*/; | |
font-weight: bold; | |
color: #444 /*{c-bup-color}*/; | |
text-shadow: 0 /*{c-bup-shadow-x}*/ 1px /*{c-bup-shadow-y}*/ 1px /*{c-bup-shadow-radius}*/ #f6f6f6 /*{c-bup-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd /*{c-bup-background-start}*/), to(#eee /*{c-bup-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fdfdfd /*{c-bup-background-start}*/, #eee /*{c-bup-background-end}*/); | |
} | |
.ui-btn-up-c a.ui-link-inherit { | |
color: #2F3E46 /*{c-bup-color}*/; | |
} | |
.ui-btn-hover-c { | |
border: 1px solid #bbb /*{c-bhover-border}*/; | |
background: #dadada /*{c-bhover-background-color}*/; | |
font-weight: bold; | |
color: #101010 /*{c-bhover-color}*/; | |
text-shadow: 0 /*{c-bhover-shadow-x}*/ 1px /*{c-bhover-shadow-y}*/ 1px /*{c-bhover-shadow-radius}*/ #fff /*{c-bhover-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#ededed /*{c-bhover-background-start}*/), to(#dadada /*{c-bhover-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #ededed /*{c-bhover-background-start}*/, #dadada /*{c-bhover-background-end}*/); | |
} | |
.ui-btn-hover-c a.ui-link-inherit { | |
color: #2F3E46 /*{c-bhover-color}*/; | |
} | |
.ui-btn-down-c { | |
border: 1px solid #808080 /*{c-bdown-border}*/; | |
background: #fdfdfd /*{c-bdown-background-color}*/; | |
font-weight: bold; | |
color: #111111 /*{c-bdown-color}*/; | |
text-shadow: 0 /*{c-bdown-shadow-x}*/ 1px /*{c-bdown-shadow-y}*/ 1px /*{c-bdown-shadow-radius}*/ #ffffff /*{c-bdown-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{c-bdown-background-start}*/), to(#fdfdfd /*{c-bdown-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #eee /*{c-bdown-background-start}*/, #fdfdfd /*{c-bdown-background-end}*/); | |
} | |
.ui-btn-down-c a.ui-link-inherit { | |
color: #2F3E46 /*{c-bdown-color}*/; | |
} | |
.ui-btn-up-c, | |
.ui-btn-hover-c, | |
.ui-btn-down-c { | |
font-family: Helvetica, Arial, sans-serif /*{c-button-font}*/; | |
text-decoration: none; | |
} | |
/* D | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-d { | |
border: 1px solid #ccc /*{d-bar-border}*/; | |
background: #bbb /*{d-bar-background-color}*/; | |
color: #333 /*{d-bar-color}*/; | |
text-shadow: 0 /*{d-bar-shadow-x}*/ 1px /*{d-bar-shadow-y}*/ 0 /*{d-bar-shadow-radius}*/ #eee /*{d-bar-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#ddd /*{d-bar-background-start}*/), to(#bbb /*{d-bar-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #ddd /*{d-bar-background-start}*/, #bbb /*{d-bar-background-end}*/); | |
} | |
.ui-bar-d, | |
.ui-bar-d input, | |
.ui-bar-d select, | |
.ui-bar-d textarea, | |
.ui-bar-d button { | |
font-family: Helvetica, Arial, sans-serif /*{d-bar-font}*/; | |
} | |
.ui-bar-d .ui-link-inherit { | |
color: #333 /*{d-bar-color}*/; | |
} | |
.ui-bar-d .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-body-d { | |
border: 1px solid #ccc /*{d-body-border}*/; | |
color: #333333 /*{d-body-color}*/; | |
text-shadow: 0 /*{d-body-shadow-x}*/ 1px /*{d-body-shadow-y}*/ 0 /*{d-body-shadow-radius}*/ #fff /*{d-body-shadow-color}*/; | |
background: #ffffff /*{d-body-background-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#fff /*{d-body-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fff /*{d-body-background-start}*/, #fff /*{d-body-background-end}*/); | |
} | |
.ui-body-d, | |
.ui-body-d input, | |
.ui-body-d select, | |
.ui-body-d textarea, | |
.ui-body-d button { | |
font-family: Helvetica, Arial, sans-serif /*{d-body-font}*/; | |
} | |
.ui-body-d .ui-link-inherit { | |
color: #333333 /*{d-body-color}*/; | |
} | |
.ui-body-d .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-btn-up-d { | |
border: 1px solid #ccc /*{d-bup-border}*/; | |
background: #fff /*{d-bup-background-color}*/; | |
font-weight: bold; | |
color: #444 /*{d-bup-color}*/; | |
text-shadow: 0 /*{d-bup-shadow-x}*/ 1px /*{d-bup-shadow-y}*/ 1px /*{d-bup-shadow-radius}*/ #fff /*{d-bup-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#fff /*{d-bup-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fff /*{d-bup-background-start}*/, #fff /*{d-bup-background-end}*/); | |
} | |
.ui-btn-up-d a.ui-link-inherit { | |
color: #333 /*{d-bup-color}*/; | |
} | |
.ui-btn-hover-d { | |
border: 1px solid #aaa /*{d-bhover-border}*/; | |
background: #eeeeee /*{d-bhover-background-color}*/; | |
font-weight: bold; | |
color: #222 /*{d-bhover-color}*/; | |
cursor: pointer; | |
text-shadow: 0 /*{d-bhover-shadow-x}*/ 1px /*{d-bhover-shadow-y}*/ 1px /*{d-bhover-shadow-radius}*/ #fff /*{d-bhover-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fdfdfd), to(#eee /*{d-bhover-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fdfdfd /*{d-bhover-background-start}*/, #eee /*{d-bhover-background-end}*/); | |
} | |
.ui-btn-hover-d a.ui-link-inherit { | |
color: #222 /*{d-bhover-color}*/; | |
} | |
.ui-btn-down-d { | |
border: 1px solid #aaaaaa /*{d-bdown-border}*/; | |
background: #ffffff /*{d-bdown-background-color}*/; | |
font-weight: bold; | |
color: #111 /*{d-bdown-color}*/; | |
text-shadow: 0 /*{d-bdown-shadow-x}*/ 1px /*{d-bdown-shadow-y}*/ 1px /*{d-bdown-shadow-radius}*/ #ffffff /*{d-bdown-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#eee /*{d-bdown-background-start}*/), to(#fff /*{d-bdown-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #eee /*{d-bdown-background-start}*/, #fff /*{d-bdown-background-end}*/); | |
} | |
.ui-btn-down-d a.ui-link-inherit { | |
color: #111 /*{d-bdown-color}*/; | |
} | |
.ui-btn-up-d, | |
.ui-btn-hover-d, | |
.ui-btn-down-d { | |
font-family: Helvetica, Arial, sans-serif /*{d-button-font}*/; | |
text-decoration: none; | |
} | |
/* E | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-bar-e { | |
border: 1px solid #F7C942 /*{e-bar-border}*/; | |
background: #fadb4e /*{e-bar-background-color}*/; | |
color: #333 /*{e-bar-color}*/; | |
text-shadow: 0 /*{e-bar-shadow-x}*/ 1px /*{e-bar-shadow-y}*/ 0 /*{e-bar-shadow-radius}*/ #fff /*{e-bar-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7 /*{e-bar-background-start}*/), to(#fadb4e /*{e-bar-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fceda7 /*{e-bar-background-start}*/, #fadb4e /*{e-bar-background-end}*/); | |
} | |
.ui-bar-e, | |
.ui-bar-e input, | |
.ui-bar-e select, | |
.ui-bar-e textarea, | |
.ui-bar-e button { | |
font-family: Helvetica, Arial, sans-serif /*{e-bar-font}*/; | |
} | |
.ui-bar-e .ui-link-inherit { | |
color: #333 /*{e-bar-color}*/; | |
} | |
.ui-bar-e .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-body-e { | |
border: 1px solid #F7C942 /*{e-body-border}*/; | |
color: #333333 /*{e-body-color}*/; | |
text-shadow: 0 /*{e-body-shadow-x}*/ 1px /*{e-body-shadow-y}*/ 0 /*{e-body-shadow-radius}*/ #fff /*{e-body-shadow-color}*/; | |
background: #faeb9e /*{e-body-background-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff /*{e-body-background-start}*/), to(#faeb9e /*{e-body-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fff /*{e-body-background-start}*/, #faeb9e /*{e-body-background-end}*/); | |
} | |
.ui-body-e, | |
.ui-body-e input, | |
.ui-body-e select, | |
.ui-body-e textarea, | |
.ui-body-e button { | |
font-family: Helvetica, Arial, sans-serif /*{e-body-font}*/; | |
} | |
.ui-body-e .ui-link-inherit { | |
color: #333333 /*{e-body-color}*/; | |
} | |
.ui-body-e .ui-link { | |
color: #2489CE /*{global-link-color}*/; | |
font-weight: bold; | |
} | |
.ui-btn-up-e { | |
border: 1px solid #F7C942 /*{e-bup-border}*/; | |
background: #fadb4e /*{e-bup-background-color}*/; | |
font-weight: bold; | |
color: #333 /*{e-bup-color}*/; | |
text-shadow: 0 /*{e-bup-shadow-x}*/ 1px /*{e-bup-shadow-y}*/ 0 /*{e-bup-shadow-radius}*/ #fff /*{e-bup-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fceda7 /*{e-bup-background-start}*/), to(#fadb4e /*{e-bup-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fceda7 /*{e-bup-background-start}*/, #fadb4e /*{e-bup-background-end}*/); | |
} | |
.ui-btn-up-e a.ui-link-inherit { | |
color: #333 /*{e-bup-color}*/; | |
} | |
.ui-btn-hover-e { | |
border: 1px solid #e79952 /*{e-bhover-border}*/; | |
background: #fbe26f /*{e-bhover-background-color}*/; | |
font-weight: bold; | |
color: #111 /*{e-bhover-color}*/; | |
text-shadow: 0 /*{e-bhover-shadow-x}*/ 1px /*{e-bhover-shadow-y}*/ 1px /*{e-bhover-shadow-radius}*/ #fff /*{e-bhover-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf0b5 /*{e-bhover-background-start}*/), to(#fbe26f /*{e-bhover-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fcf0b5 /*{e-bhover-background-start}*/, #fbe26f /*{e-bhover-background-end}*/); | |
} | |
.ui-btn-hover-e a.ui-link-inherit { | |
color: #333 /*{e-bhover-color}*/; | |
} | |
.ui-btn-down-e { | |
border: 1px solid #F7C942 /*{e-bdown-border}*/; | |
background: #fceda7 /*{e-bdown-background-color}*/; | |
font-weight: bold; | |
color: #111 /*{e-bdown-color}*/; | |
text-shadow: 0 /*{e-bdown-shadow-x}*/ 1px /*{e-bdown-shadow-y}*/ 1px /*{e-bdown-shadow-radius}*/ #ffffff /*{e-bdown-shadow-color}*/; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#fadb4e /*{e-bdown-background-start}*/), to(#fceda7 /*{e-bdown-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #fadb4e /*{e-bdown-background-start}*/, #fceda7 /*{e-bdown-background-end}*/); | |
} | |
.ui-btn-down-e a.ui-link-inherit { | |
color: #333 /*{e-bdown-color}*/; | |
} | |
.ui-btn-up-e, | |
.ui-btn-hover-e, | |
.ui-btn-down-e { | |
font-family: Helvetica, Arial, sans-serif /*{e-button-font}*/; | |
text-decoration: none; | |
} | |
/* Structure */ | |
/* links within "buttons" | |
-----------------------------------------------------------------------------------------------------------*/ | |
a.ui-link-inherit { | |
text-decoration: none !important; | |
} | |
/* links and their different states | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-link{ | |
color: #2489CE /*{global-link-color}*/ | |
} | |
.ui-link:hover{ | |
color: #2489CE /*{global-link-hover}*/ | |
} | |
.ui-link:active{ | |
color: #2489CE /*{global-link-active}*/ | |
} | |
.ui-link:visited{ | |
color: #2489CE /*{global-link-visited}*/ | |
} | |
/* Active class used as the "on" state across all themes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-active { | |
border: 1px solid #155678 /*{global-active-border}*/; | |
background: #4596ce /*{global-active-background-color}*/; | |
font-weight: bold; | |
color: #fff /*{global-active-color}*/; | |
cursor: pointer; | |
text-shadow: 0 /*{global-active-shadow-x}*/ -1px /*{global-active-shadow-y}*/ 1px /*{global-active-shadow-radius}*/ #145072 /*{global-active-shadow-color}*/; | |
text-decoration: none; | |
background-image: -webkit-gradient(linear, left top, left bottom, from(#85bae4 /*{global-active-background-start}*/), to(#5393c5 /*{global-active-background-end}*/)); /* Saf4+, Chrome */ | |
background-image: -webkit-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* Chrome 10+, Saf5.1+ */ | |
background-image: -moz-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* FF3.6 */ | |
background-image: -ms-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* IE10 */ | |
background-image: -o-linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); /* Opera 11.10+ */ | |
background-image: linear-gradient(top, #85bae4 /*{global-active-background-start}*/, #5393c5 /*{global-active-background-end}*/); | |
outline: none; | |
font-family: Helvetica, Arial, sans-serif /*{global-active-font}*/; | |
} | |
.ui-btn-active a.ui-link-inherit { | |
color: #fff /*{global-active-color}*/; | |
} | |
/* button inner top highlight | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-inner { | |
border-top: 1px solid #fff; | |
border-color: rgba(255,255,255,.3); | |
} | |
/* corner rounding classes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-corner-tl { | |
-moz-border-radius-topleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-tr { | |
-moz-border-radius-topright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-bl { | |
-moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-br { | |
-moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-top { | |
-moz-border-radius-topleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
-moz-border-radius-topright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-bottom { | |
-moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
-moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-right { | |
-moz-border-radius-topright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
border-top-right-radius: .6em /*{global-radii-blocks}*/; | |
-moz-border-radius-bottomright: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-right-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-left { | |
-moz-border-radius-topleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
border-top-left-radius: .6em /*{global-radii-blocks}*/; | |
-moz-border-radius-bottomleft: .6em /*{global-radii-blocks}*/; | |
-webkit-border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
border-bottom-left-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-all { | |
-moz-border-radius: .6em /*{global-radii-blocks}*/; | |
-webkit-border-radius: .6em /*{global-radii-blocks}*/; | |
border-radius: .6em /*{global-radii-blocks}*/; | |
} | |
.ui-corner-none { | |
-moz-border-radius: 0; | |
-webkit-border-radius: 0; | |
border-radius: 0; | |
} | |
/* Interaction cues | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-disabled { | |
opacity: .3; | |
} | |
.ui-disabled, | |
.ui-disabled a { | |
cursor: default; | |
} | |
/* Icons | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-icon, | |
.ui-icon-searchfield:after { | |
background: #666 /*{global-icon-color}*/; | |
background: rgba(0,0,0,.4) /*{global-icon-disc}*/; | |
background-image: url(images/icons-18-white.png) /*{global-icon-set}*/; | |
background-repeat: no-repeat; | |
-moz-border-radius: 9px; | |
-webkit-border-radius: 9px; | |
border-radius: 9px; | |
} | |
/* Alt icon color | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-icon-alt { | |
background: #fff; | |
background: rgba(255,255,255,.3); | |
background-image: url(images/icons-18-black.png); | |
background-repeat: no-repeat; | |
} | |
/* HD/"retina" sprite | |
-----------------------------------------------------------------------------------------------------------*/ | |
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), | |
only screen and (min--moz-device-pixel-ratio: 1.5), | |
only screen and (min-resolution: 240dpi) { | |
.ui-icon-plus, .ui-icon-minus, .ui-icon-delete, .ui-icon-arrow-r, | |
.ui-icon-arrow-l, .ui-icon-arrow-u, .ui-icon-arrow-d, .ui-icon-check, | |
.ui-icon-gear, .ui-icon-refresh, .ui-icon-forward, .ui-icon-back, | |
.ui-icon-grid, .ui-icon-star, .ui-icon-alert, .ui-icon-info, .ui-icon-home, .ui-icon-search, .ui-icon-searchfield:after, | |
.ui-icon-checkbox-off, .ui-icon-checkbox-on, .ui-icon-radio-off, .ui-icon-radio-on { | |
background-image: url(images/icons-36-white.png); | |
-moz-background-size: 776px 18px; | |
-o-background-size: 776px 18px; | |
-webkit-background-size: 776px 18px; | |
background-size: 776px 18px; | |
} | |
.ui-icon-alt { | |
background-image: url(images/icons-36-black.png); | |
} | |
} | |
/* plus minus */ | |
.ui-icon-plus { | |
background-position: -0 50%; | |
} | |
.ui-icon-minus { | |
background-position: -36px 50%; | |
} | |
/* delete/close */ | |
.ui-icon-delete { | |
background-position: -72px 50%; | |
} | |
/* arrows */ | |
.ui-icon-arrow-r { | |
background-position: -108px 50%; | |
} | |
.ui-icon-arrow-l { | |
background-position: -144px 50%; | |
} | |
.ui-icon-arrow-u { | |
background-position: -180px 50%; | |
} | |
.ui-icon-arrow-d { | |
background-position: -216px 50%; | |
} | |
/* misc */ | |
.ui-icon-check { | |
background-position: -252px 50%; | |
} | |
.ui-icon-gear { | |
background-position: -288px 50%; | |
} | |
.ui-icon-refresh { | |
background-position: -324px 50%; | |
} | |
.ui-icon-forward { | |
background-position: -360px 50%; | |
} | |
.ui-icon-back { | |
background-position: -396px 50%; | |
} | |
.ui-icon-grid { | |
background-position: -432px 50%; | |
} | |
.ui-icon-star { | |
background-position: -468px 50%; | |
} | |
.ui-icon-alert { | |
background-position: -504px 50%; | |
} | |
.ui-icon-info { | |
background-position: -540px 50%; | |
} | |
.ui-icon-home { | |
background-position: -576px 50%; | |
} | |
.ui-icon-search, | |
.ui-icon-searchfield:after { | |
background-position: -612px 50%; | |
} | |
.ui-icon-checkbox-off { | |
background-position: -684px 50%; | |
} | |
.ui-icon-checkbox-on { | |
background-position: -648px 50%; | |
} | |
.ui-icon-radio-off { | |
background-position: -756px 50%; | |
} | |
.ui-icon-radio-on { | |
background-position: -720px 50%; | |
} | |
/* checks,radios */ | |
.ui-checkbox .ui-icon { | |
-moz-border-radius: 3px; | |
-webkit-border-radius: 3px; | |
border-radius: 3px; | |
} | |
.ui-icon-checkbox-off, | |
.ui-icon-radio-off { | |
background-color: transparent; | |
} | |
.ui-checkbox-on .ui-icon, | |
.ui-radio-on .ui-icon { | |
background-color: #4596ce /*{global-active-background-color}*/; /* NOTE: this hex should match the active state color. It's repeated here for cascade */ | |
} | |
/* loading icon */ | |
.ui-icon-loading { | |
background-image: url(images/ajax-loader.png); | |
width: 40px; | |
height: 40px; | |
-moz-border-radius: 20px; | |
-webkit-border-radius: 20px; | |
border-radius: 20px; | |
background-size: 35px 35px; | |
} | |
/* Button corner classes | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-btn-corner-tl { | |
-moz-border-radius-topleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-tr { | |
-moz-border-radius-topright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-bl { | |
-moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-br { | |
-moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-top { | |
-moz-border-radius-topleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
-moz-border-radius-topright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-bottom { | |
-moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
-moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-right { | |
-moz-border-radius-topright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
border-top-right-radius: 1em /*{global-radii-buttons}*/; | |
-moz-border-radius-bottomright: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-right-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-left { | |
-moz-border-radius-topleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
border-top-left-radius: 1em /*{global-radii-buttons}*/; | |
-moz-border-radius-bottomleft: 1em /*{global-radii-buttons}*/; | |
-webkit-border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
border-bottom-left-radius: 1em /*{global-radii-buttons}*/; | |
} | |
.ui-btn-corner-all { | |
-moz-border-radius: 1em /*{global-radii-buttons}*/; | |
-webkit-border-radius: 1em /*{global-radii-buttons}*/; | |
border-radius: 1em /*{global-radii-buttons}*/; | |
} | |
/* radius clip workaround for cleaning up corner trapping */ | |
.ui-corner-tl, | |
.ui-corner-tr, | |
.ui-corner-bl, | |
.ui-corner-br, | |
.ui-corner-top, | |
.ui-corner-bottom, | |
.ui-corner-right, | |
.ui-corner-left, | |
.ui-corner-all, | |
.ui-btn-corner-tl, | |
.ui-btn-corner-tr, | |
.ui-btn-corner-bl, | |
.ui-btn-corner-br, | |
.ui-btn-corner-top, | |
.ui-btn-corner-bottom, | |
.ui-btn-corner-right, | |
.ui-btn-corner-left, | |
.ui-btn-corner-all { | |
-webkit-background-clip: padding-box; | |
-moz-background-clip: padding; | |
background-clip: padding-box; | |
} | |
/* Overlay / modal | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-overlay { | |
background: #666; | |
opacity: .5; | |
filter: Alpha(Opacity=50); | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
} | |
.ui-overlay-shadow { | |
-moz-box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
-webkit-box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
box-shadow: 0px 0px 12px rgba(0,0,0,.6); | |
} | |
.ui-shadow { | |
-moz-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/; | |
-webkit-box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/; | |
box-shadow: 0px 1px 4px /*{global-box-shadow-size}*/ rgba(0,0,0,.3) /*{global-box-shadow-color}*/; | |
} | |
.ui-bar-a .ui-shadow, | |
.ui-bar-b .ui-shadow , | |
.ui-bar-c .ui-shadow { | |
-moz-box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
-webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
box-shadow: 0px 1px 0 rgba(255,255,255,.3); | |
} | |
.ui-shadow-inset { | |
-moz-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
-webkit-box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
box-shadow: inset 0px 1px 4px rgba(0,0,0,.2); | |
} | |
.ui-icon-shadow { | |
-moz-box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
-webkit-box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
box-shadow: 0px 1px 0 rgba(255,255,255,.4); | |
} | |
/* Focus state - set here for specificity | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-focus { | |
-moz-box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/; | |
-webkit-box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/; | |
box-shadow: 0px 0px 12px #387bbe /*{global-active-background-color}*/; | |
} | |
/* unset box shadow in browsers that don't do it right | |
-----------------------------------------------------------------------------------------------------------*/ | |
.ui-mobile-nosupport-boxshadow * { | |
-moz-box-shadow: none !important; | |
-webkit-box-shadow: none !important; | |
box-shadow: none !important; | |
} | |
/* ...and bring back focus */ | |
.ui-mobile-nosupport-boxshadow .ui-focus { | |
outline-width: 2px; | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) and GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* some unsets - more probably needed */ | |
.ui-mobile, .ui-mobile body { height: 100%; } | |
.ui-mobile fieldset, .ui-page { padding: 0; margin: 0; } | |
.ui-mobile a img, .ui-mobile fieldset { border: 0; } | |
/* responsive page widths */ | |
.ui-mobile-viewport { margin: 0; overflow-x: hidden; -webkit-text-size-adjust: none; -ms-text-size-adjust:none; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } | |
/* "page" containers - full-screen views, one should always be in view post-pageload */ | |
.ui-mobile [data-role=page], .ui-mobile [data-role=dialog], .ui-page { top: 0; left: 0; width: 100%; min-height: 100%; position: absolute; display: none; border: 0; } | |
.ui-mobile .ui-page-active { display: block; overflow: visible; } | |
/* on ios4, setting focus on the page element causes flashing during transitions when there is an outline, so we turn off outlines */ | |
.ui-page { outline: none; } | |
/* native overflow scrolling */ | |
.ui-page.ui-mobile-touch-overflow, | |
.ui-mobile-touch-overflow.ui-native-fixed .ui-content { | |
overflow: auto; | |
height: 100%; | |
-webkit-overflow-scrolling: touch; | |
-moz-overflow-scrolling: touch; | |
-o-overflow-scrolling: touch; | |
-ms-overflow-scrolling: touch; | |
overflow-scrolling: touch; | |
} | |
.ui-page.ui-mobile-touch-overflow, | |
.ui-page.ui-mobile-touch-overflow * { | |
/* some level of transform keeps elements from blinking out of visibility on iOS */ | |
-webkit-transform: rotateY(0); | |
} | |
.ui-page.ui-mobile-pre-transition { | |
display: block; | |
} | |
/* loading screen */ | |
.ui-loading .ui-mobile-viewport { overflow: hidden !important; } | |
.ui-loading .ui-loader { display: block; } | |
.ui-loading .ui-page { overflow: hidden; } | |
.ui-loader { display: none; position: absolute; opacity: .85; z-index: 100; left: 50%; width: 200px; margin-left: -130px; margin-top: -35px; padding: 10px 30px; } | |
.ui-loader h1 { font-size: 15px; text-align: center; } | |
.ui-loader .ui-icon { position: static; display: block; opacity: .9; margin: 0 auto; width: 35px; height: 35px; background-color: transparent; } | |
/*fouc*/ | |
.ui-mobile-rendering > * { visibility: hidden; } | |
/*headers, content panels*/ | |
.ui-bar, .ui-body { position: relative; padding: .4em 15px; overflow: hidden; display: block; clear:both; } | |
.ui-bar { font-size: 16px; margin: 0; } | |
.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; } | |
.ui-header, .ui-footer { display: block; } | |
.ui-page .ui-header, .ui-page .ui-footer { position: relative; } | |
.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; } | |
.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; } | |
.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; } | |
/*content area*/ | |
.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; } | |
.ui-page-fullscreen .ui-content { padding:0; } | |
/* native fixed headers and footers */ | |
.ui-mobile-touch-overflow.ui-page.ui-native-fixed, | |
.ui-mobile-touch-overflow.ui-page.ui-native-fullscreen { | |
overflow: visible; | |
} | |
.ui-mobile-touch-overflow.ui-native-fixed .ui-header, | |
.ui-mobile-touch-overflow.ui-native-fixed .ui-footer { | |
position: fixed; | |
left: 0; | |
right: 0; | |
top: 0; | |
z-index: 200; | |
} | |
.ui-mobile-touch-overflow.ui-page.ui-native-fixed .ui-footer { | |
top: auto; | |
bottom: 0; | |
} | |
.ui-mobile-touch-overflow.ui-native-fixed .ui-content { | |
padding-top: 2.5em; | |
padding-bottom: 3em; | |
top: 0; | |
bottom: 0; | |
height: auto; | |
position: absolute; | |
} | |
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-content { | |
padding-top: 0; | |
padding-bottom: 0; | |
} | |
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-header, | |
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-footer { | |
opacity: .9; | |
} | |
.ui-native-bars-hidden { | |
display: none; | |
} | |
/* icons sizing */ | |
.ui-icon { width: 18px; height: 18px; } | |
/* fullscreen class on ui-content div */ | |
.ui-fullscreen { } | |
.ui-fullscreen img { max-width: 100%; } | |
/* non-js content hiding */ | |
.ui-nojs { position: absolute; left: -9999px; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.spin { | |
-webkit-transform: rotate(360deg); | |
-webkit-animation-name: spin; | |
-webkit-animation-duration: 1s; | |
-webkit-animation-iteration-count: infinite; | |
-webkit-animation-timing-function: linear; | |
} | |
@-webkit-keyframes spin { | |
from {-webkit-transform: rotate(0deg);} | |
to {-webkit-transform: rotate(360deg);} | |
} | |
/* Transitions from jQtouch (with small modifications): http://www.jqtouch.com/ | |
Built by David Kaneda and maintained by Jonathan Stark. | |
*/ | |
.in, .out { | |
-webkit-animation-timing-function: ease-in-out; | |
-webkit-animation-duration: 350ms; | |
} | |
.slide.out { | |
-webkit-transform: translateX(-100%); | |
-webkit-animation-name: slideouttoleft; | |
} | |
.slide.in { | |
-webkit-transform: translateX(0); | |
-webkit-animation-name: slideinfromright; | |
} | |
.slide.out.reverse { | |
-webkit-transform: translateX(100%); | |
-webkit-animation-name: slideouttoright; | |
} | |
.slide.in.reverse { | |
-webkit-transform: translateX(0); | |
-webkit-animation-name: slideinfromleft; | |
} | |
.slideup.out { | |
-webkit-animation-name: dontmove; | |
z-index: 0; | |
} | |
.slideup.in { | |
-webkit-transform: translateY(0); | |
-webkit-animation-name: slideinfrombottom; | |
z-index: 10; | |
} | |
.slideup.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
.slideup.out.reverse { | |
-webkit-transform: translateY(100%); | |
z-index: 10; | |
-webkit-animation-name: slideouttobottom; | |
} | |
.slidedown.out { | |
-webkit-animation-name: dontmove; | |
z-index: 0; | |
} | |
.slidedown.in { | |
-webkit-transform: translateY(0); | |
-webkit-animation-name: slideinfromtop; | |
z-index: 10; | |
} | |
.slidedown.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
.slidedown.out.reverse { | |
-webkit-transform: translateY(-100%); | |
z-index: 10; | |
-webkit-animation-name: slideouttotop; | |
} | |
@-webkit-keyframes slideinfromright { | |
from { -webkit-transform: translateX(100%); } | |
to { -webkit-transform: translateX(0); } | |
} | |
@-webkit-keyframes slideinfromleft { | |
from { -webkit-transform: translateX(-100%); } | |
to { -webkit-transform: translateX(0); } | |
} | |
@-webkit-keyframes slideouttoleft { | |
from { -webkit-transform: translateX(0); } | |
to { -webkit-transform: translateX(-100%); } | |
} | |
@-webkit-keyframes slideouttoright { | |
from { -webkit-transform: translateX(0); } | |
to { -webkit-transform: translateX(100%); } | |
} | |
@-webkit-keyframes slideinfromtop { | |
from { -webkit-transform: translateY(-100%); } | |
to { -webkit-transform: translateY(0); } | |
} | |
@-webkit-keyframes slideinfrombottom { | |
from { -webkit-transform: translateY(100%); } | |
to { -webkit-transform: translateY(0); } | |
} | |
@-webkit-keyframes slideouttobottom { | |
from { -webkit-transform: translateY(0); } | |
to { -webkit-transform: translateY(100%); } | |
} | |
@-webkit-keyframes slideouttotop { | |
from { -webkit-transform: translateY(0); } | |
to { -webkit-transform: translateY(-100%); } | |
} | |
@-webkit-keyframes fadein { | |
from { opacity: 0; } | |
to { opacity: 1; } | |
} | |
@-webkit-keyframes fadeout { | |
from { opacity: 1; } | |
to { opacity: 0; } | |
} | |
.fade.out { | |
z-index: 0; | |
-webkit-animation-name: fadeout; | |
} | |
.fade.in { | |
opacity: 1; | |
z-index: 10; | |
-webkit-animation-name: fadein; | |
} | |
/* The properties in this rule are only necessary for the 'flip' transition. | |
* We need specify the perspective to create a projection matrix. This will add | |
* some depth as the element flips. The depth number represents the distance of | |
* the viewer from the z-plane. According to the CSS3 spec, 1000 is a moderate | |
* value. | |
*/ | |
.viewport-flip { | |
-webkit-perspective: 1000; | |
position: absolute; | |
} | |
.ui-mobile-viewport-transitioning, | |
.ui-mobile-viewport-transitioning .ui-page { | |
width: 100%; | |
height: 100%; | |
overflow: hidden; | |
} | |
.flip { | |
-webkit-animation-duration: .65s; | |
-webkit-backface-visibility:hidden; | |
-webkit-transform:translateX(0); /* Needed to work around an iOS 3.1 bug that causes listview thumbs to disappear when -webkit-visibility:hidden is used. */ | |
} | |
.flip.out { | |
-webkit-transform: rotateY(-180deg) scale(.8); | |
-webkit-animation-name: flipouttoleft; | |
} | |
.flip.in { | |
-webkit-transform: rotateY(0) scale(1); | |
-webkit-animation-name: flipinfromleft; | |
} | |
/* Shake it all about */ | |
.flip.out.reverse { | |
-webkit-transform: rotateY(180deg) scale(.8); | |
-webkit-animation-name: flipouttoright; | |
} | |
.flip.in.reverse { | |
-webkit-transform: rotateY(0) scale(1); | |
-webkit-animation-name: flipinfromright; | |
} | |
@-webkit-keyframes flipinfromright { | |
from { -webkit-transform: rotateY(-180deg) scale(.8); } | |
to { -webkit-transform: rotateY(0) scale(1); } | |
} | |
@-webkit-keyframes flipinfromleft { | |
from { -webkit-transform: rotateY(180deg) scale(.8); } | |
to { -webkit-transform: rotateY(0) scale(1); } | |
} | |
@-webkit-keyframes flipouttoleft { | |
from { -webkit-transform: rotateY(0) scale(1); } | |
to { -webkit-transform: rotateY(-180deg) scale(.8); } | |
} | |
@-webkit-keyframes flipouttoright { | |
from { -webkit-transform: rotateY(0) scale(1); } | |
to { -webkit-transform: rotateY(180deg) scale(.8); } | |
} | |
/* Hackish, but reliable. */ | |
@-webkit-keyframes dontmove { | |
from { opacity: 1; } | |
to { opacity: 1; } | |
} | |
.pop { | |
-webkit-transform-origin: 50% 50%; | |
} | |
.pop.in { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
-webkit-animation-name: popin; | |
z-index: 10; | |
} | |
.pop.in.reverse { | |
z-index: 0; | |
-webkit-animation-name: dontmove; | |
} | |
.pop.out.reverse { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
-webkit-animation-name: popout; | |
z-index: 10; | |
} | |
@-webkit-keyframes popin { | |
from { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
} | |
to { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
} | |
} | |
@-webkit-keyframes popout { | |
from { | |
-webkit-transform: scale(1); | |
opacity: 1; | |
} | |
to { | |
-webkit-transform: scale(.2); | |
opacity: 0; | |
} | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* content configurations. */ | |
.ui-grid-a, .ui-grid-b, .ui-grid-c, .ui-grid-d { overflow: hidden; } | |
.ui-block-a, .ui-block-b, .ui-block-c, .ui-block-d, .ui-block-e { margin: 0; padding: 0; border: 0; float: left; min-height:1px;} | |
/* grid solo: 100 - single item fallback */ | |
.ui-grid-solo .ui-block-a { width: 100%; float: none; } | |
/* grid a: 50/50 */ | |
.ui-grid-a .ui-block-a, .ui-grid-a .ui-block-b { width: 50%; } | |
.ui-grid-a .ui-block-a { clear: left; } | |
/* grid b: 33/33/33 */ | |
.ui-grid-b .ui-block-a, .ui-grid-b .ui-block-b, .ui-grid-b .ui-block-c { width: 33.333%; } | |
.ui-grid-b .ui-block-a { clear: left; } | |
/* grid c: 25/25/25/25 */ | |
.ui-grid-c .ui-block-a, .ui-grid-c .ui-block-b, .ui-grid-c .ui-block-c, .ui-grid-c .ui-block-d { width: 25%; } | |
.ui-grid-c .ui-block-a { clear: left; } | |
/* grid d: 20/20/20/20/20 */ | |
.ui-grid-d .ui-block-a, .ui-grid-d .ui-block-b, .ui-grid-d .ui-block-c, .ui-grid-d .ui-block-d, .ui-grid-d .ui-block-e { width: 20%; } | |
.ui-grid-d .ui-block-a { clear: left; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
/* fixed page header & footer configuration */ | |
.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; } | |
.ui-header-fixed, .ui-footer-fixed { | |
z-index: 1000; | |
-webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */ | |
} | |
.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; } | |
.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-navbar { overflow: hidden; } | |
.ui-navbar ul, .ui-navbar-expanded ul { list-style:none; padding: 0; margin: 0; position: relative; display: block; border: 0;} | |
.ui-navbar-collapsed ul { float: left; width: 75%; margin-right: -2px; } | |
.ui-navbar-collapsed .ui-navbar-toggle { float: left; width: 25%; } | |
.ui-navbar li.ui-navbar-truncate { position: absolute; left: -9999px; top: -9999px; } | |
.ui-navbar li .ui-btn, .ui-navbar .ui-navbar-toggle .ui-btn { display: block; font-size: 12px; text-align: center; margin: 0; border-right-width: 0; } | |
.ui-navbar li .ui-btn { margin-right: -1px; } | |
.ui-navbar li .ui-btn:last-child { margin-right: 0; } | |
.ui-header .ui-navbar li .ui-btn, .ui-header .ui-navbar .ui-navbar-toggle .ui-btn, | |
.ui-footer .ui-navbar li .ui-btn, .ui-footer .ui-navbar .ui-navbar-toggle .ui-btn { border-top-width: 0; border-bottom-width: 0; } | |
.ui-navbar .ui-btn-inner { padding-left: 2px; padding-right: 2px; } | |
.ui-navbar-noicons li .ui-btn .ui-btn-inner, .ui-navbar-noicons .ui-navbar-toggle .ui-btn-inner { padding-top: .8em; padding-bottom: .9em; } | |
/*expanded page styles*/ | |
.ui-navbar-expanded .ui-btn { margin: 0; font-size: 14px; } | |
.ui-navbar-expanded .ui-btn-inner { padding-left: 5px; padding-right: 5px; } | |
.ui-navbar-expanded .ui-btn-icon-top .ui-btn-inner { padding: 45px 5px 15px; text-align: center; } | |
.ui-navbar-expanded .ui-btn-icon-top .ui-icon { top: 15px; } | |
.ui-navbar-expanded .ui-btn-icon-bottom .ui-btn-inner { padding: 15px 5px 45px; text-align: center; } | |
.ui-navbar-expanded .ui-btn-icon-bottom .ui-icon { bottom: 15px; } | |
.ui-navbar-expanded li .ui-btn .ui-btn-inner { min-height: 2.5em; } | |
.ui-navbar-expanded .ui-navbar-noicons .ui-btn .ui-btn-inner { padding-top: 1.8em; padding-bottom: 1.9em; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-btn { display: block; text-align: center; cursor:pointer; position: relative; margin: .5em 5px; padding: 0; } | |
.ui-btn:focus, .ui-btn:active { outline: none; } | |
.ui-header .ui-btn, .ui-footer .ui-btn, .ui-bar .ui-btn { display: inline-block; font-size: 13px; margin: 0; } | |
.ui-btn-inline { display: inline-block; } | |
.ui-btn-inner { padding: .6em 25px; display: block; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; position: relative; zoom: 1; } | |
.ui-header .ui-btn-inner, .ui-footer .ui-btn-inner, .ui-bar .ui-btn-inner { padding: .4em 8px .5em; } | |
.ui-btn-icon-notext { width: 24px; height: 24px; } | |
.ui-btn-icon-notext .ui-btn-inner { padding: 2px 1px 2px 3px; } | |
.ui-btn-icon-notext .ui-btn-text { position: absolute; left: -999px; } | |
.ui-btn-icon-left .ui-btn-inner { padding-left: 33px; } | |
.ui-header .ui-btn-icon-left .ui-btn-inner, | |
.ui-footer .ui-btn-icon-left .ui-btn-inner, | |
.ui-bar .ui-btn-icon-left .ui-btn-inner { padding-left: 27px; } | |
.ui-btn-icon-right .ui-btn-inner { padding-right: 33px; } | |
.ui-header .ui-btn-icon-right .ui-btn-inner, | |
.ui-footer .ui-btn-icon-right .ui-btn-inner, | |
.ui-bar .ui-btn-icon-right .ui-btn-inner { padding-right: 27px; } | |
.ui-btn-icon-top .ui-btn-inner { padding-top: 33px; } | |
.ui-header .ui-btn-icon-top .ui-btn-inner, | |
.ui-footer .ui-btn-icon-top .ui-btn-inner, | |
.ui-bar .ui-btn-icon-top .ui-btn-inner { padding-top: 27px; } | |
.ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 33px; } | |
.ui-header .ui-btn-icon-bottom .ui-btn-inner, | |
.ui-footer .ui-btn-icon-bottom .ui-btn-inner, | |
.ui-bar .ui-btn-icon-bottom .ui-btn-inner { padding-bottom: 27px; } | |
/*btn icon positioning*/ | |
.ui-btn-icon-notext .ui-icon { display: block; } | |
.ui-btn-icon-left .ui-icon, .ui-btn-icon-right .ui-icon { position: absolute; top: 50%; margin-top: -9px; } | |
.ui-btn-icon-top .ui-icon, .ui-btn-icon-bottom .ui-icon { position: absolute; left: 50%; margin-left: -9px; } | |
.ui-btn-icon-left .ui-icon { left: 10px; } | |
.ui-btn-icon-right .ui-icon { right: 10px; } | |
.ui-btn-icon-top .ui-icon { top: 10px; } | |
.ui-btn-icon-bottom .ui-icon { bottom: 10px; } | |
.ui-header .ui-btn-icon-left .ui-icon, | |
.ui-footer .ui-btn-icon-left .ui-icon, | |
.ui-bar .ui-btn-icon-left .ui-icon { left: 4px; } | |
.ui-header .ui-btn-icon-right .ui-icon, | |
.ui-footer .ui-btn-icon-right .ui-icon, | |
.ui-bar .ui-btn-icon-right .ui-icon { right: 4px; } | |
.ui-header .ui-btn-icon-top .ui-icon, | |
.ui-footer .ui-btn-icon-top .ui-icon, | |
.ui-bar .ui-btn-icon-top .ui-icon { top: 4px; } | |
.ui-header .ui-btn-icon-bottom .ui-icon, | |
.ui-footer .ui-btn-icon-bottom .ui-icon, | |
.ui-bar .ui-btn-icon-bottom .ui-icon { bottom: 4px; } | |
/*hiding native button,inputs */ | |
.ui-btn-hidden { position: absolute; top: 0; left: 0; width: 100%; height: 100%; -webkit-appearance: button; opacity: .1; cursor: pointer; background: transparent; font-size: 1px; border: none; line-height: 999px; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-collapsible { margin: .5em 0; } | |
.ui-collapsible-heading { font-size: 16px; display: block; margin: 0 -8px; padding: 0; border-width: 0 0 1px 0; position: relative; } | |
.ui-collapsible-heading a { text-align: left; margin: 0; } | |
.ui-collapsible-heading a .ui-btn-inner { padding-left: 40px; } | |
.ui-collapsible-heading a span.ui-btn { position: absolute; left: 6px; top: 50%; margin: -12px 0 0 0; width: 20px; height: 20px; padding: 1px 0px 1px 2px; text-indent: -9999px; } | |
.ui-collapsible-heading a span.ui-btn .ui-btn-inner { padding: 10px 0; } | |
.ui-collapsible-heading a span.ui-btn .ui-icon { left: 0; margin-top: -10px; } | |
.ui-collapsible-heading-status { position:absolute; left:-9999px; } | |
.ui-collapsible-content { | |
display: block; | |
margin: 0 -8px; | |
padding: 10px 16px; | |
border-top: none; /* Overrides ui-btn-up-* */ | |
background-image: none; /* Overrides ui-btn-up-* */ | |
font-weight: normal; /* Overrides ui-btn-up-* */ | |
} | |
.ui-collapsible-content-collapsed { display: none; } | |
.ui-collapsible-set { margin: .5em 0; } | |
.ui-collapsible-set .ui-collapsible { margin: -1px 0 0; } | |
/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-controlgroup, fieldset.ui-controlgroup { padding: 0; margin: .5em 0 1em; } | |
.ui-bar .ui-controlgroup { margin: 0 .3em; } | |
.ui-controlgroup-label { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; } | |
.ui-controlgroup-controls { display: block; width: 95%;} | |
.ui-controlgroup li { list-style: none; } | |
.ui-controlgroup-vertical .ui-btn, | |
.ui-controlgroup-vertical .ui-checkbox, .ui-controlgroup-vertical .ui-radio { margin: 0; border-bottom-width: 0; } | |
.ui-controlgroup-vertical .ui-controlgroup-last { border-bottom-width: 1px; } | |
.ui-controlgroup-horizontal { padding: 0; } | |
.ui-controlgroup-horizontal .ui-btn { display: inline-block; margin: 0 -5px 0 0; } | |
.ui-controlgroup-horizontal .ui-checkbox, .ui-controlgroup-horizontal .ui-radio { float: left; margin: 0 -1px 0 0; } | |
.ui-controlgroup-horizontal .ui-checkbox .ui-btn, .ui-controlgroup-horizontal .ui-radio .ui-btn, | |
.ui-controlgroup-horizontal .ui-checkbox:last-child, .ui-controlgroup-horizontal .ui-radio:last-child { margin-right: 0; } | |
.ui-controlgroup-horizontal .ui-controlgroup-last { margin-right: 0; } | |
.ui-controlgroup .ui-checkbox label, .ui-controlgroup .ui-radio label { font-size: 16px; } | |
/* conflicts with listview.. | |
.ui-controlgroup .ui-btn-icon-notext { width: 30px; height: 30px; text-indent: -9999px; } | |
.ui-controlgroup .ui-btn-icon-notext .ui-btn-inner { padding: 5px 6px 5px 5px; } | |
*/ | |
@media all and (min-width: 450px){ | |
.ui-controlgroup-label { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.ui-controlgroup-controls { width: 60%; display: inline-block; } | |
} /* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-dialog { min-height: 480px; } | |
.ui-dialog .ui-header, .ui-dialog .ui-content, .ui-dialog .ui-footer { margin: 15px; position: relative; } | |
.ui-dialog .ui-header, .ui-dialog .ui-footer { z-index: 10; width: auto; } | |
.ui-dialog .ui-content, .ui-dialog .ui-footer { margin-top: -15px; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-checkbox, .ui-radio { position:relative; margin: .2em 0 .5em; z-index: 1; } | |
.ui-checkbox .ui-btn, .ui-radio .ui-btn { margin: 0; text-align: left; z-index: 2; } | |
.ui-checkbox .ui-btn-inner, .ui-radio .ui-btn-inner { white-space: normal; } | |
.ui-checkbox .ui-btn-icon-left .ui-btn-inner,.ui-radio .ui-btn-icon-left .ui-btn-inner { padding-left: 45px; } | |
.ui-checkbox .ui-btn-icon-right .ui-btn-inner, .ui-radio .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } | |
.ui-checkbox .ui-icon, .ui-radio .ui-icon { top: 1.1em; } | |
.ui-checkbox .ui-btn-icon-left .ui-icon, .ui-radio .ui-btn-icon-left .ui-icon {left: 15px; } | |
.ui-checkbox .ui-btn-icon-right .ui-icon, .ui-radio .ui-btn-icon-right .ui-icon {right: 15px; } | |
/* input, label positioning */ | |
.ui-checkbox input,.ui-radio input { position:absolute; left:20px; top:50%; width: 10px; height: 10px; margin:-5px 0 0 0; outline: 0 !important; z-index: 1; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-field-contain { padding: 1.5em 0; margin: 0; border-bottom-width: 1px; overflow: visible; } | |
.ui-field-contain:first-child { border-top-width: 0; } | |
@media all and (min-width: 450px){ | |
.ui-field-contain { border-width: 0; padding: 0; margin: 1em 0; } | |
} /* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-select { display: block; position: relative; } | |
.ui-select select { position: absolute; left: -9999px; top: -9999px; } | |
.ui-select .ui-btn { overflow: hidden; } | |
.ui-select .ui-btn select { cursor: pointer; -webkit-appearance: button; left: 0; top:0; width: 100%; min-height: 1.5em; min-height: 100%; height: 3em; max-height: 100%; opacity: 0; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter: alpha(opacity=0); z-index: 2; } | |
@-moz-document url-prefix() {.ui-select .ui-btn select { opacity: 0.0001; }} | |
.ui-select .ui-btn select.ui-select-nativeonly { opacity: 1; text-indent: 0; } | |
.ui-select .ui-btn-icon-right .ui-btn-inner { padding-right: 45px; } | |
.ui-select .ui-btn-icon-right .ui-icon { right: 15px; } | |
/* labels */ | |
label.ui-select { font-size: 16px; line-height: 1.4; font-weight: normal; margin: 0 0 .3em; display: block; } | |
/*listbox*/ | |
.ui-select .ui-btn-text, .ui-selectmenu .ui-btn-text { display: block; min-height: 1em; } | |
.ui-select .ui-btn-text { text-overflow: ellipsis; overflow: hidden;} | |
.ui-selectmenu { position: absolute; padding: 0; z-index: 100 !important; width: 80%; max-width: 350px; padding: 6px; } | |
.ui-selectmenu .ui-listview { margin: 0; } | |
.ui-selectmenu .ui-btn.ui-li-divider { cursor: default; } | |
.ui-selectmenu-hidden { top: -9999px; left: -9999px; } | |
.ui-selectmenu-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 99; } | |
.ui-screen-hidden, .ui-selectmenu-list .ui-li .ui-icon { display: none; } | |
.ui-selectmenu-list .ui-li .ui-icon { display: block; } | |
.ui-li.ui-selectmenu-placeholder { display: none; } | |
.ui-selectmenu .ui-header .ui-title { margin: 0.6em 46px 0.8em; } | |
@media all and (min-width: 450px){ | |
label.ui-select { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
.ui-select { width: 60%; display: inline-block; } | |
} | |
/* when no placeholder is defined in a multiple select, the header height doesn't even extend past the close button. this shim's content in there */ | |
.ui-selectmenu .ui-header h1:after { content: '.'; visibility: hidden; }/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
label.ui-input-text { font-size: 16px; line-height: 1.4; display: block; font-weight: normal; margin: 0 0 .3em; } | |
input.ui-input-text, textarea.ui-input-text { background-image: none; padding: .4em; line-height: 1.4; font-size: 16px; display: block; width: 95%; } | |
input.ui-input-text { -webkit-appearance: none; } | |
textarea.ui-input-text { height: 50px; -webkit-transition: height 200ms linear; -moz-transition: height 200ms linear; -o-transition: height 200ms linear; transition: height 200ms linear; } | |
.ui-input-search { padding: 0 30px; width: 77%; background-image: none; position: relative; } | |
.ui-icon-searchfield:after { position: absolute; left: 7px; top: 50%; margin-top: -9px; content: ""; width: 18px; height: 18px; opacity: .5; } | |
.ui-input-search input.ui-input-text { border: none; width: 98%; padding: .4em 0; margin: 0; display: block; background: transparent none; outline: 0 !important; } | |
.ui-input-search .ui-input-clear { position: absolute; right: 0; top: 50%; margin-top: -14px; } | |
.ui-input-search .ui-input-clear-hidden { display: none; } | |
/* orientation adjustments - incomplete!*/ | |
@media all and (min-width: 450px){ | |
label.ui-input-text { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0 } | |
input.ui-input-text, | |
textarea.ui-input-text, | |
.ui-input-search { width: 60%; display: inline-block; } | |
.ui-input-search { width: 50%; } | |
.ui-input-search input.ui-input-text { width: 98%; /*echos rule from above*/ } | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
.ui-listview { margin: 0; counter-reset: listnumbering; } | |
.ui-content .ui-listview { margin: -15px; } | |
.ui-content .ui-listview-inset { margin: 1em 0; } | |
.ui-listview, .ui-li { list-style:none; padding:0; } | |
.ui-li, .ui-li.ui-field-contain { display: block; margin:0; position: relative; overflow: visible; text-align: left; border-width: 0; border-top-width: 1px; } | |
.ui-li .ui-btn-text { position: relative; z-index: 1; } | |
.ui-li .ui-btn-text a.ui-link-inherit { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-divider, .ui-li-static { padding: .5em 15px; font-size: 14px; font-weight: bold; } | |
.ui-li-divider { counter-reset: listnumbering; } | |
ol.ui-listview .ui-link-inherit:before, ol.ui-listview .ui-li-static:before, .ui-li-dec { font-size: .8em; display: inline-block; padding-right: .3em; font-weight: normal;counter-increment: listnumbering; content: counter(listnumbering) ". "; } | |
ol.ui-listview .ui-li-jsnumbering:before { content: "" !important; } /* to avoid chance of duplication */ | |
.ui-listview-inset .ui-li { border-right-width: 1px; border-left-width: 1px; } | |
.ui-li:last-child, .ui-li.ui-field-contain:last-child { border-bottom-width: 1px; } | |
.ui-li>.ui-btn-inner { display: block; position: relative; padding: 0; } | |
.ui-li .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li { padding: .7em 15px .7em 15px; display: block; } | |
.ui-li-has-thumb .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-thumb { min-height: 60px; padding-left: 100px; } | |
.ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon { min-height: 20px; padding-left: 40px; } | |
.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-count { padding-right: 45px; } | |
.ui-li-has-arrow .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow { padding-right: 30px; } | |
.ui-li-has-arrow.ui-li-has-count .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-arrow.ui-li-has-count { padding-right: 75px; } | |
.ui-li-heading { font-size: 16px; font-weight: bold; display: block; margin: .6em 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-desc { font-size: 12px; font-weight: normal; display: block; margin: -.5em 0 .6em; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } | |
.ui-li-thumb, .ui-li-icon { position: absolute; left: 1px; top: 0; max-height: 80px; max-width: 80px; } | |
.ui-li-icon { max-height: 40px; max-width: 40px; left: 10px; top: .9em; } | |
.ui-li-thumb, .ui-li-icon, .ui-li-content { float: left; margin-right: 10px; } | |
.ui-li-aside { float: right; width: 50%; text-align: right; margin: .3em 0; } | |
@media all and (min-width: 480px){ | |
.ui-li-aside { width: 45%; } | |
} | |
.ui-li-divider { cursor: default; } | |
.ui-li-has-alt .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-alt { padding-right: 95px; } | |
.ui-li-has-count .ui-li-count { position: absolute; font-size: 11px; font-weight: bold; padding: .2em .5em; top: 50%; margin-top: -.9em; right: 38px; } | |
.ui-li-divider .ui-li-count, .ui-li-static .ui-li-count { right: 10px; } | |
.ui-li-has-alt .ui-li-count { right: 55px; } | |
.ui-li-link-alt { position: absolute; width: 40px; height: 100%; border-width: 0; border-left-width: 1px; top: 0; right: 0; margin: 0; padding: 0; z-index: 2; } | |
.ui-li-link-alt .ui-btn { overflow: hidden; position: absolute; right: 8px; top: 50%; margin: -11px 0 0 0; border-bottom-width: 1px; z-index: -1;} | |
.ui-li-link-alt .ui-btn-inner { padding: 0; height: 100%; position: absolute; width: 100%; top: 0; left: 0;} | |
.ui-li-link-alt .ui-btn .ui-icon { right: 50%; margin-right: -9px; } | |
.ui-listview * .ui-btn-inner > .ui-btn > .ui-btn-inner { border-top: 0px; } | |
.ui-listview-filter { border-width: 0; overflow: hidden; margin: -15px -15px 15px -15px } | |
.ui-listview-filter .ui-input-search { margin: 5px; width: auto; display: block; } | |
.ui-listview-filter-inset { margin: -15px -5px -15px -5px; background: transparent; } | |
.ui-li.ui-screen-hidden{display:none;} | |
/* Odd iPad positioning issue. */ | |
@media only screen and (min-device-width: 768px) and (max-device-width: 1024px) { | |
.ui-li .ui-btn-text { overflow: visible; } | |
}/* | |
* jQuery Mobile Framework | |
* Copyright (c) jQuery Project | |
* Dual licensed under the MIT (MIT-LICENSE.txt) or GPL (GPL-LICENSE.txt) licenses. | |
*/ | |
label.ui-slider { display: block; } | |
input.ui-slider-input { display: inline-block; width: 50px; } | |
select.ui-slider-switch { display: none; } | |
div.ui-slider { position: relative; display: inline-block; overflow: visible; height: 15px; padding: 0; margin: 0 2% 0 20px; top: 4px; width: 66%; } | |
a.ui-slider-handle { position: absolute; z-index: 10; top: 50%; width: 28px; height: 28px; margin-top: -15px; margin-left: -15px; } | |
a.ui-slider-handle .ui-btn-inner { padding-left: 0; padding-right: 0; } | |
@media all and (min-width: 480px){ | |
label.ui-slider { vertical-align: top; display: inline-block; width: 20%; margin: 0 2% 0 0; } | |
div.ui-slider { width: 45%; } | |
} | |
div.ui-slider-switch { height: 32px; overflow: hidden; margin-left: 0; } | |
div.ui-slider-inneroffset { margin-left: 50%; position: absolute; top: 1px; height: 100%; width: 50%; } | |
a.ui-slider-handle-snapping { -webkit-transition: left 100ms linear; } | |
div.ui-slider-labelbg { position: absolute; top:0; margin: 0; border-width: 0; } | |
div.ui-slider-switch div.ui-slider-labelbg-a { width: 60%; height: 100%; left: 0; } | |
div.ui-slider-switch div.ui-slider-labelbg-b { width: 60%; height: 100%; right: 0; } | |
.ui-slider-switch-a div.ui-slider-labelbg-a, .ui-slider-switch-b div.ui-slider-labelbg-b { z-index: -1; } | |
.ui-slider-switch-a div.ui-slider-labelbg-b, .ui-slider-switch-b div.ui-slider-labelbg-a { z-index: 0; } | |
div.ui-slider-switch a.ui-slider-handle { z-index: 20; width: 101%; height: 32px; margin-top: -18px; margin-left: -101%; } | |
span.ui-slider-label { width: 100%; position: absolute;height: 32px; font-size: 16px; text-align: center; line-height: 2; background: none; border-color: transparent; } | |
span.ui-slider-label-a { left: -100%; margin-right: -1px } | |
span.ui-slider-label-b { right: -100%; margin-left: -1px } | |
div.hasDatepicker{display:block;padding:0;overflow:visible;margin:8px 0;} | |
.ui-datepicker{overflow:visible;margin:0;max-width:500px;} | |
.ui-datepicker .ui-datepicker-header{position:relative;padding:.4em 0;border-bottom:0;font-weight:bold;} | |
.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next{padding:1px 0 1px 2px;position:absolute;top:.5em;margin-top:0;text-indent:-9999px;} | |
.ui-datepicker .ui-datepicker-prev{left:6px;} | |
.ui-datepicker .ui-datepicker-next{right:6px;} | |
.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center;} | |
.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0;} | |
.ui-datepicker select.ui-datepicker-month-year{width:100%;} | |
.ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year{width:49%;} | |
.ui-datepicker table{width:100%;border-collapse:collapse;margin:0;} | |
.ui-datepicker td{border-width:1px;padding:0;text-align:center;} | |
.ui-datepicker td span, .ui-datepicker td a{display:block;padding:.2em 0;font-weight:bold;margin:0;border-width:0;text-align:center;text-decoration:none;} | |
.ui-datepicker-calendar th{padding-top:.3em;padding-bottom:.3em;} | |
.ui-datepicker-calendar th span, .ui-datepicker-calendar span.ui-state-default{opacity:.3;} | |
.ui-datepicker-calendar td a{padding-top:.5em;padding-bottom:.5em;} | |
.min-width-480px div.hasDatepicker{width:63%;display:inline-block;margin:0;} | |
<?php | |
header('Content-type: text/css'); | |
ob_start("compress"); | |
function compress($buffer) { | |
/* remove comments */ | |
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer); | |
/* remove tabs, spaces, newlines, etc. */ | |
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $buffer); | |
return $buffer; | |
} | |
echo ' | |
.ui-li-thumb, .ui-li-icon { position: relative; } | |
.ui-navbar { | |
width: 100%; | |
} | |
.ui-btn-inner { | |
white-space: normal !important; | |
} | |
.ui-li-heading { | |
white-space: normal !important; | |
} | |
.ui-listview-filter { | |
margin: 0 !important; | |
} | |
.ui-icon-navigation { | |
background-image: url(images/113-navigation.png); | |
background-position: 1px 0; | |
} | |
.ui-icon-beaker { | |
background-image: url(images/91-beaker-2.png); | |
background-position: 1px 0; | |
} | |
#footer { | |
text-size: 0.75em; | |
text-align: center; | |
} | |
body { | |
background-color: #F0F0F0; | |
} | |
#jqm-homeheader { | |
text-align: center; | |
} | |
.viaPoints { | |
display: none; | |
text-size: 0.2em; | |
} | |
.min-width-480px .viaPoints { | |
display: inline; | |
} | |
#extrainfo { | |
visibility: hidden; | |
display: none; | |
} | |
#servicewarning { | |
padding: 1em; | |
margin-bottom: 0.5em; | |
text-size: 0.2em; | |
background-color: #FF9; | |
-moz-border-radius: 15px; | |
border-radius: 15px; | |
} | |
#footer { | |
clear:both; | |
text-align:center; | |
} | |
// source http://webaim.org/techniques/skipnav/ | |
#skip a, #skip a:hover, #skip a:visited | |
{ | |
position:absolute; | |
left:0px; | |
top:-500px; | |
width:1px; | |
height:1px; | |
overflow:hidden; | |
} | |
#skip a:active, #skip a:focus | |
{ | |
position:static; | |
width:auto; | |
height:auto; | |
}'; | |
//if (false) | |
echo ' | |
// adaptive layout from jQuery Mobile docs site | |
.type-interior .content-secondary { | |
border-right: 0; | |
border-left: 0; | |
margin: 10px -15px 0; | |
background: #fff; | |
border-top: 1px solid #ccc; | |
} | |
.type-home .ui-content { | |
margin-top: 5px; | |
} | |
.type-interior .ui-content { | |
padding-bottom: 0; | |
} | |
.content-secondary .ui-collapsible-contain { | |
padding: 10px 15px; | |
} | |
.content-secondary .ui-collapsible-heading { | |
margin: 0 0 30px; | |
} | |
.content-secondary .ui-collapsible-heading-collapsed, | |
.content-secondary .ui-collapsible-content { | |
padding:0; | |
margin: 0; | |
} | |
/* hires ahoy */ | |
@media all and (min-width: 650px){ | |
.content-secondary { | |
text-align: left; | |
float: left; | |
width: 45%; | |
background: none; | |
border-top: 0; | |
} | |
.content-secondary, | |
.type-interior .content-secondary { | |
margin: 30px 0 20px 2%; | |
padding: 20px 4% 0 0; | |
background: none; | |
} | |
.type-index .content-secondary { | |
padding: 0; | |
} | |
.type-index .content-secondary .ui-listview { | |
margin: 0; | |
} | |
.content-primary { | |
width: 45%; | |
float: right; | |
margin-top: 30px; | |
margin-right: 1%; | |
padding-right: 1%; | |
} | |
.content-primary ul:first-child { | |
margin-top: 0; | |
} | |
.type-interior .content-primary { | |
padding: 1.5em 6% 3em 0; | |
margin: 0; | |
} | |
/* fix up the collapsibles - expanded on desktop */ | |
.content-secondary .ui-collapsible-heading { | |
display: none; | |
} | |
.content-secondary .ui-collapsible-contain { | |
margin:0; | |
} | |
.content-secondary .ui-collapsible-content { | |
display: block; | |
margin: 0; | |
padding: 0; | |
} | |
.type-interior .content-secondary .ui-li-divider { | |
padding-top: 1em; | |
padding-bottom: 1em; | |
} | |
.type-interior .content-secondary { | |
margin: 0; | |
padding: 0; | |
} | |
} | |
@media all and (min-width: 750px){ | |
.type-home .ui-content, | |
.type-interior .ui-content { | |
background-position: 39%; | |
} | |
.content-secondary { | |
width: 34%; | |
} | |
.content-primary { | |
width: 56%; | |
padding-right: 1%; | |
} | |
.type-interior .ui-content { | |
background-position: 34%; | |
} | |
} | |
@media all and (min-width: 1200px){ | |
.type-home .ui-content{ | |
background-position: 38.5%; | |
} | |
.type-interior .ui-content { | |
background-position: 30%; | |
} | |
.content-secondary { | |
width: 30%; | |
padding-right:6%; | |
margin: 30px 0 20px 5%; | |
} | |
.type-interior .content-secondary { | |
margin: 0; | |
padding: 0; | |
} | |
.content-primary { | |
width: 50%; | |
margin-right: 5%; | |
padding-right: 3%; | |
} | |
.type-interior .content-primary { | |
width: 60%; | |
} | |
} | |
'; | |
ob_end_flush(); | |
?> | |
#!/bin/bash | #!/bin/bash |
#dotcloud postinstall | #dotcloud postinstall |
curl http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ | curl http://s3-ap-southeast-1.amazonaws.com/busresources/cbrfeed.zip \ |
-o /home/dotcloud/current/cbrfeed.zip | -o /home/dotcloud/current/cbrfeed.zip |
wget http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ | curl http://s3-ap-southeast-1.amazonaws.com/busresources/Graph.obj \ |
-O /tmp/Graph.obj | -o /tmp/Graph.obj |
#db setup | #db setup |
#curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz | #curl https://github.com/maxious/ACTBus-ui/raw/master/transitdata.cbrfeed.sql.gz -o transitdata.cbrfeed.sql.gz |
#curl https://github.com/maxious/ACTBus-ui/raw/master/lib/postgis.sql -o postgis.sql | #curl https://github.com/maxious/ACTBus-ui/raw/master/lib/postgis.sql -o postgis.sql |
#createlang -d transitdata plpgsql | #createlang -d transitdata plpgsql |
#psql -d transitdata -f postgis.sql | #psql -d transitdata -f postgis.sql |
#gunzip /var/www/transitdata.cbrfeed.sql.gz | #gunzip /var/www/transitdata.cbrfeed.sql.gz |
#psql -d transitdata -f transitdata.cbrfeed.sql | #psql -d transitdata -f transitdata.cbrfeed.sql |
#createuser transitdata -SDRP | #createuser transitdata -SDRP |
#password transitdata | #password transitdata |
#psql -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\" | #psql -c \"GRANT SELECT ON TABLE agency,calendar,calendar_dates,routes,stop_times,stops,trips TO transitdata;\" |
<?php | |
header('Content-Type: application/vnd.google-earth.kml+xml'); | |
include ('../include/common.inc.php'); | |
echo '<?xml version="1.0" encoding="UTF-8"?> | |
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"><Document>'; | |
echo ' | |
<Style id="yellowLineGreenPoly"> | |
<LineStyle> | |
<color>7f00ff00</color> | |
<width>4</width> | |
</LineStyle> | |
<PolyStyle> | |
<color>7f00ffff</color> | |
</PolyStyle> | |
</Style>'; | |
$route = getRoute($routeid); | |
echo "\n<Placemark>\n"; | |
$link = curPageURL()."/../trip.php?routeid=".htmlspecialchars ($route["route_id"]); | |
echo "<name>".$route['route_short_name']."</name>"; | |
echo '<atom:link href="'.$link.'"/>'; | |
echo '<description><![CDATA[ <a href="'.$link.'">'.$route['route_short_name']." ".$route['route_long_name']."</a>]]> </description>"; | |
echo "<styleUrl>#yellowLineGreenPoly</styleUrl>"; | |
$trips = getRouteTrips($routeid); | |
echo getTripShape($trips[0]['trip_id']); | |
echo "</Placemark>\n</Document></kml>\n"; | |
?> | |
<?php | |
include ('../include/common.inc.php'); | |
header('Content-type: application/vnd.google-earth.kml+xml'); | |
//http://wiki.openstreetmap.org/wiki/OpenLayers_Dynamic_KML | |
// Creates the KML/XML Document. | |
$dom = new DOMDocument('1.0', 'UTF-8'); | |
// Creates the root KML element and appends it to the root document. | |
$node = $dom->createElementNS('http://earth.google.com/kml/2.1', 'kml'); | |
$parNode = $dom->appendChild($node); | |
// Creates a KML Document element and append it to the KML element. | |
$dnode = $dom->createElement('Document'); | |
$docNode = $parNode->appendChild($dnode); | |
if ($suburb != "") $result_stops = getStopsBySuburb($suburb); | |
else $result_stops = getStops(); | |
foreach ($result_stops as $stop) { | |
$description = 'http://bus.lambdacomplex.org/' . 'stop.php?stopid=' . $stop['stop_id'] . " <br>"; | |
// Creates a Placemark and append it to the Document. | |
$node = $dom->createElement('Placemark'); | |
$placeNode = $docNode->appendChild($node); | |
// Creates an id attribute and assign it the value of id column. | |
$placeNode->setAttribute('id', 'placemark' . $stop['stop_id']); | |
// Create name, and description elements and assigns them the values of the name and address columns from the results. | |
$nameNode = $dom->createElement('name', htmlentities($stop['stop_name'])); | |
$descriptionNode = $dom->createElement('description', $description); | |
$placeNode->appendChild($nameNode); | |
$placeNode->appendChild($descriptionNode); | |
// Creates a Point element. | |
$pointNode = $dom->createElement('Point'); | |
$placeNode->appendChild($pointNode); | |
// Creates a coordinates element and gives it the value of the lng and lat columns from the results. | |
$coorStr = $stop['stop_lon'] . ',' . $stop['stop_lat']; | |
$coorNode = $dom->createElement('coordinates', $coorStr); | |
$pointNode->appendChild($coorNode); | |
} | |
$kmlOutput = $dom->saveXML(); | |
echo $kmlOutput; | |
?> | |
<?php | |
require $basePath.'lib/openid.php'; | |
$openid = new LightOpenID($_SERVER['HTTP_HOST']); | |
function login() | |
{ | |
global $openid; | |
if(!$openid->mode) { | |
$openid->required = array('contact/email'); | |
$openid->identity = 'https://www.google.com/accounts/o8/id'; | |
header('Location: ' . $openid->authUrl()); | |
} | |
} | |
function auth() | |
{ | |
if ($_SESSION['authed'] == true) return true; | |
global $openid; | |
if($openid->mode) { | |
$attr = $openid->getAttributes(); | |
if ($attr["contact/email"] != "maxious@gmail.com") { | |
die("Access Denied"); | |
} else { | |
$_SESSION['authed'] = true; | |
} | |
} else { | |
login(); | |
} | |
} | |
?> |
<?php | <?php |
if (php_uname('n') == "actbus-www") { | |
$conn = pg_connect("dbname=transitdata user=transitdata password=transitdata host=bus-main.lambdacomplex.org"); | /* |
} else if (isDebugServer()) { | * Copyright 2010,2011 Alexander Sadleir |
$conn = pg_connect("dbname=transitdata user=postgres password=snmc"); | |
} else { | Licensed under the Apache License, Version 2.0 (the "License"); |
$conn = pg_connect("dbname=transitdata user=transitdata password=transitdata "); | you may not use this file except in compliance with the License. |
} | You may obtain a copy of the License at |
if (!$conn) { | |
die("A database error occurred.\n"); | http://www.apache.org/licenses/LICENSE-2.0 |
} | |
Unless required by applicable law or agreed to in writing, software | |
function databaseError($errMsg) { | 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. | |
*/ | |
if (php_uname('n') == "actbus-www") { | |
$conn = new PDO("pgsql:dbname=transitdata;user=transitdata;password=transitdata;host=bus-main.lambdacomplex.org"); | |
} else if (isDebugServer()) { | |
$conn = new PDO("pgsql:dbname=transitdata;user=postgres;password=snmc;host=localhost"); | |
} else { | |
$conn = new PDO("pgsql:dbname=transitdata;user=transitdata;password=transitdata;host=localhost"); | |
} | |
if (!$conn) { | |
die("A database error occurred.\n"); | |
} | |
function databaseError($errMsg) { | |
die($errMsg); | die($errMsg); |
} | } |
include('db/route-dao.inc.php'); | |
include('db/trip-dao.inc.php'); | |
include('db/stop-dao.inc.php'); | |
?> | |
include ('db/route-dao.inc.php'); | |
include ('db/trip-dao.inc.php'); | |
include ('db/stop-dao.inc.php'); | |
include ('db/servicealert-dao.inc.php'); | |
?> | |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
// SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',') | // SELECT array_to_string(array(SELECT REPLACE(name_2006, ',', '\,') as name FROM suburbs order by name), ',') |
$suburbs = explode(",", "Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla"); | $suburbs = explode(",", "Acton,Ainslie,Amaroo,Aranda,Banks,Barton,Belconnen,Bonner,Bonython,Braddon,Bruce,Calwell,Campbell,Chapman,Charnwood,Chifley,Chisholm,City,Conder,Cook,Curtin,Deakin,Dickson,Downer,Duffy,Dunlop,Evatt,Fadden,Farrer,Fisher,Florey,Flynn,Forrest,Franklin,Fraser,Fyshwick,Garran,Gilmore,Giralang,Gordon,Gowrie,Greenway,Griffith,Gungahlin,Hackett,Hall,Harrison,Hawker,Higgins,Holder,Holt,Hughes,Hume,Isaacs,Isabella Plains,Kaleen,Kambah,Kingston,Latham,Lawson,Lyneham,Lyons,Macarthur,Macgregor,Macquarie,Mawson,McKellar,Melba,Mitchell,Monash,Narrabundah,Ngunnawal,Nicholls,Oaks Estate,O'Connor,O'Malley,Oxley,Page,Palmerston,Parkes,Pearce,Phillip,Pialligo,Red Hill,Reid,Richardson,Rivett,Russell,Scullin,Spence,Stirling,Symonston,Tharwa,Theodore,Torrens,Turner,Wanniassa,Waramanga,Watson,Weetangera,Weston,Yarralumla"); |
function staticmap($mapPoints, $zoom = 0, $markerImage = "iconb", $collapsible = true) | |
{ | function staticmap($mapPoints, $collapsible = true, $twotone = false, $path = false, $numbered = false) { |
$width = 300; | |
$height = 300; | $markers = ""; |
$metersperpixel[9] = 305.492 * $width; | $height = 300; |
$metersperpixel[10] = 152.746 * $width; | $width = $height; |
$metersperpixel[11] = 76.373 * $width; | $index = 0; |
$metersperpixel[12] = 38.187 * $width; | if (sizeof($mapPoints) < 1) |
$metersperpixel[13] = 19.093 * $width; | return "map error"; |
$metersperpixel[14] = 9.547 * $width; | if (sizeof($mapPoints) === 1) { |
$metersperpixel[15] = 4.773 * $width; | $markers = "markers={$mapPoints[0][0]},{$mapPoints[0][1]}"; |
$metersperpixel[16] = 2.387 * $width; | } else { |
// $metersperpixel[17]=1.193*$width; | if (!$numbered) { |
$center = ""; | $markers = "markers="; |
$markers = ""; | } |
$minlat = 999; | if ($path) { |
$minlon = 999; | $markers.= "markers={$mapPoints[0][0]},{$mapPoints[0][1]}&path="; |
$maxlat = 0; | } |
$maxlon = 0; | foreach ($mapPoints as $index => $mapPoint) { |
if (sizeof($mapPoints) < 1) return "map error"; | if ($twotone && $index == 0) { |
if (sizeof($mapPoints) === 1) { | $markers = "markerd=color:red|".$mapPoint[0] . "," . $mapPoint[1]."&markers="; |
if ($zoom == 0) $zoom = 14; | } else { |
$markers.= "{$mapPoints[0][0]},{$mapPoints[0][1]},$markerimage"; | if ($numbered) { |
$center = "{$mapPoints[0][0]},{$mapPoints[0][1]}"; | $label = ($index > 9 ? 9 : $index); |
} | $markers.= "markers=label:$label|" . $mapPoint[0] . "," . $mapPoint[1]; |
else { | if ($index + 1 != sizeof($mapPoints)) { |
foreach ($mapPoints as $index => $mapPoint) { | $markers.= "&"; |
$markers.= $mapPoint[0] . "," . $mapPoint[1] . "," . $markerImage . ($index + 1); | } |
if ($index + 1 != sizeof($mapPoints)) $markers.= "|"; | } else { |
if ($mapPoint[0] < $minlat) $minlat = $mapPoint[0]; | $markers.= $mapPoint[0] . "," . $mapPoint[1]; |
if ($mapPoint[0] > $maxlat) $maxlat = $mapPoint[0]; | if ($index + 1 != sizeof($mapPoints)) { |
if ($mapPoint[1] < $minlon) $minlon = $mapPoint[1]; | $markers.= "|"; |
if ($mapPoint[1] > $maxlon) $maxlon = $mapPoint[1]; | } |
$totalLat+= $mapPoint[0]; | } |
$totalLon+= $mapPoint[1]; | $index++; |
} | } |
if ($zoom == 0) { | } |
$mapwidthinmeters = distance($minlat, $minlon, $minlat, $maxlon); | } |
foreach (array_reverse($metersperpixel, true) as $zoomLevel => $maxdistance) { | $output = ""; |
if ($zoom == 0 && $mapwidthinmeters < ($maxdistance + 50)) $zoom = $zoomLevel; | if ($collapsible) |
} | $output.= '<div class="map" data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>'; |
} | if (isIOSDevice()) $output.= '<img class="hiresmap" src="http://maps.googleapis.com/maps/api/staticmap?size=' . $width . 'x' . $height . '&' . $markers . '&scale=2&sensor=true" width=' . $width . ' height=' . $height . '>'; |
$center = $totalLat / sizeof($mapPoints) . "," . $totalLon / sizeof($mapPoints); | else $output.= '<img class="lowresmap" src="http://maps.googleapis.com/maps/api/staticmap?size=' . $width . 'x' . $height . '&' . $markers . '&scale=1&format=jpg&sensor=true" width=' . $width . ' height=' . $height . '>'; |
} | |
$output = ""; | if ($collapsible) |
if ($collapsible) $output.= '<div class="map" data-role="collapsible" data-collapsed="true"><h3>Open Map...</h3>'; | $output.= '</div>'; |
$output.= '<img class="map" src="' . curPageURL() . '/lib/staticmaplite/staticmap.php?center=' . $center . '&zoom=' . $zoom . '&size=' . $width . 'x' . $height . '&markers=' . | return $output; |
$markers . '" width=' . $width . ' height=' . $height . '>'; | |
if ($collapsible) $output.= '</div>'; | |
return $output; | |
} | |
function distance($lat1, $lng1, $lat2, $lng2, $roundLargeValues = false) | |
{ | |
$pi80 = M_PI / 180; | |
$lat1*= $pi80; | |
$lng1*= $pi80; | |
$lat2*= $pi80; | |
$lng2*= $pi80; | |
$r = 6372.797; // mean radius of Earth in km | |
$dlat = $lat2 - $lat1; | |
$dlng = $lng2 - $lng1; | |
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2); | |
$c = 2 * atan2(sqrt($a) , sqrt(1 - $a)); | |
$km = $r * $c; | |
if ($roundLargeValues) { | |
if ($km < 1) return floor($km * 1000); | |
else return round($km,2)."k"; | |
} else return floor($km * 1000); | |
} | } |
function decodePolylineToArray($encoded) | function distance($lat1, $lng1, $lat2, $lng2, $roundLargeValues = false) { |
{ | $pi80 = M_PI / 180; |
// source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5 | $lat1*= $pi80; |
$length = strlen($encoded); | $lng1*= $pi80; |
$index = 0; | $lat2*= $pi80; |
$points = array(); | $lng2*= $pi80; |
$lat = 0; | $r = 6372.797; // mean radius of Earth in km |
$lng = 0; | $dlat = $lat2 - $lat1; |
while ($index < $length) { | $dlng = $lng2 - $lng1; |
// Temporary variable to hold each ASCII byte. | $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2); |
$b = 0; | $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); |
// The encoded polyline consists of a latitude value followed by a | $km = $r * $c; |
// longitude value. They should always come in pairs. Read the | if ($roundLargeValues) { |
// latitude value first. | if ($km < 1) |
$shift = 0; | return floor($km * 1000); |
$result = 0; | else |
do { | return round($km, 2) . "k"; |
// The `ord(substr($encoded, $index++))` statement returns the ASCII | } |
// code for the character at $index. Subtract 63 to get the original | else |
// value. (63 was added to ensure proper ASCII characters are displayed | return floor($km * 1000); |
// in the encoded polyline string, which is `human` readable) | |
$b = ord(substr($encoded, $index++)) - 63; | |
// AND the bits of the byte with 0x1f to get the original 5-bit `chunk. | |
// Then left shift the bits by the required amount, which increases | |
// by 5 bits each time. | |
// OR the value into $results, which sums up the individual 5-bit chunks | |
// into the original value. Since the 5-bit chunks were reversed in | |
// order during encoding, reading them in this way ensures proper | |
// summation. | |
$result|= ($b & 0x1f) << $shift; | |
$shift+= 5; | |
} | |
// Continue while the read byte is >= 0x20 since the last `chunk` | |
// was not OR'd with 0x20 during the conversion process. (Signals the end) | |
while ($b >= 0x20); | |
// Check if negative, and convert. (All negative values have the last bit | |
// set) | |
$dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1)); | |
// Compute actual latitude since value is offset from previous value. | |
$lat+= $dlat; | |
// The next values will correspond to the longitude for this point. | |
$shift = 0; | |
$result = 0; | |
do { | |
$b = ord(substr($encoded, $index++)) - 63; | |
$result|= ($b & 0x1f) << $shift; | |
$shift+= 5; | |
} while ($b >= 0x20); | |
$dlng = (($result & 1) ? ~($result >> 1) : ($result >> 1)); | |
$lng+= $dlng; | |
// The actual latitude and longitude values were multiplied by | |
// 1e5 before encoding so that they could be converted to a 32-bit | |
// integer representation. (With a decimal accuracy of 5 places) | |
// Convert back to original values. | |
$points[] = array( | |
$lat * 1e-5, | |
$lng * 1e-5 | |
); | |
} | |
return $points; | |
} | } |
function geocode($query, $giveOptions) | |
{ | function decodePolylineToArray($encoded) { |
global $cloudmadeAPIkey; | // source: http://latlongeeks.com/forum/viewtopic.php?f=4&t=5 |
$url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?query=" . urlencode($query) . "&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true"; | $length = strlen($encoded); |
$contents = json_decode(getPage($url)); | $index = 0; |
if ($giveOptions) return $contents->features; | $points = array(); |
elseif (isset($contents->features[0]->centroid)) return $contents->features[0]->centroid->coordinates[0] . "," . $contents->features[0]->centroid->coordinates[1]; | $lat = 0; |
else return ""; | $lng = 0; |
while ($index < $length) { | |
// Temporary variable to hold each ASCII byte. | |
$b = 0; | |
// The encoded polyline consists of a latitude value followed by a | |
// longitude value. They should always come in pairs. Read the | |
// latitude value first. | |
$shift = 0; | |
$result = 0; | |
do { | |
// The `ord(substr($encoded, $index++))` statement returns the ASCII | |
// code for the character at $index. Subtract 63 to get the original | |
// value. (63 was added to ensure proper ASCII characters are displayed | |
// in the encoded polyline string, which is `human` readable) | |
$b = ord(substr($encoded, $index++)) - 63; | |
// AND the bits of the byte with 0x1f to get the original 5-bit `chunk. | |
// Then left shift the bits by the required amount, which increases | |
// by 5 bits each time. | |
// OR the value into $results, which sums up the individual 5-bit chunks | |
// into the original value. Since the 5-bit chunks were reversed in | |
// order during encoding, reading them in this way ensures proper | |
// summation. | |
$result|= ($b & 0x1f) << $shift; | |
$shift+= 5; | |
} | |
// Continue while the read byte is >= 0x20 since the last `chunk` | |
// was not OR'd with 0x20 during the conversion process. (Signals the end) | |
while ($b >= 0x20); | |
// Check if negative, and convert. (All negative values have the last bit | |
// set) | |
$dlat = (($result & 1) ? ~($result >> 1) : ($result >> 1)); | |
// Compute actual latitude since value is offset from previous value. | |
$lat+= $dlat; | |
// The next values will correspond to the longitude for this point. | |
$shift = 0; | |
$result = 0; | |
do { | |
$b = ord(substr($encoded, $index++)) - 63; | |
$result|= ($b & 0x1f) << $shift; | |
$shift+= 5; | |
} while ($b >= 0x20); | |
$dlng = (($result & 1) ? ~($result >> 1) : ($result >> 1)); | |
$lng+= $dlng; | |
// The actual latitude and longitude values were multiplied by | |
// 1e5 before encoding so that they could be converted to a 32-bit | |
// integer representation. (With a decimal accuracy of 5 places) | |
// Convert back to original values. | |
$points[] = array( | |
$lat * 1e-5, | |
$lng * 1e-5 | |
); | |
} | |
return $points; | |
} | } |
function reverseGeocode($lat, $lng) | |
{ | function geocode($query, $giveOptions) { |
global $cloudmadeAPIkey; | global $cloudmadeAPIkey; |
$url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?around=" . $lat . "," . $lng . "&distance=closest&object_type=road"; | $url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?query=" . urlencode($query) . "&bbox=-35.5,149.00,-35.15,149.1930&return_location=true&bbox_only=true"; |
$contents = json_decode(getPage($url)); | $contents = json_decode(getPage($url)); |
return $contents->features[0]->properties->name; | if ($giveOptions) |
return $contents->features; | |
elseif (isset($contents->features[0]->centroid)) | |
return $contents->features[0]->centroid->coordinates[0] . "," . $contents->features[0]->centroid->coordinates[1]; | |
else | |
return ""; | |
} | } |
function reverseGeocode($lat, $lng) { | |
global $cloudmadeAPIkey; | |
$url = "http://geocoding.cloudmade.com/$cloudmadeAPIkey/geocoding/v2/find.js?around=" . $lat . "," . $lng . "&distance=closest&object_type=road"; | |
$contents = json_decode(getPage($url)); | |
return $contents->features[0]->properties->name; | |
} | |
?> | ?> |
<?php | <?php |
function getPage($url) | |
{ | /* |
debug($url, "json"); | * Copyright 2010,2011 Alexander Sadleir |
$ch = curl_init($url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | Licensed under the Apache License, Version 2.0 (the "License"); |
curl_setopt($ch, CURLOPT_HEADER, 0); | you may not use this file except in compliance with the License. |
curl_setopt($ch, CURLOPT_TIMEOUT, 45); | You may obtain a copy of the License at |
$page = curl_exec($ch); | |
if (curl_errno($ch)) { | http://www.apache.org/licenses/LICENSE-2.0 |
echo "<font color=red> Database temporarily unavailable: "; | |
echo curl_errno($ch) . " " . curl_error($ch); | Unless required by applicable law or agreed to in writing, software |
if (isDebug()) { | distributed under the License is distributed on an "AS IS" BASIS, |
echo $url; | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
} | See the License for the specific language governing permissions and |
echo "</font><br>"; | limitations under the License. |
} | */ |
curl_close($ch); | |
debug(print_r($page,true),"json"); | function getPage($url) { |
return $page; | debug($url, "json"); |
$ch = curl_init($url); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_HEADER, 0); | |
curl_setopt($ch, CURLOPT_TIMEOUT, 45); | |
$page = curl_exec($ch); | |
if (curl_errno($ch)) { | |
echo "<font color=red> Database temporarily unavailable: "; | |
echo curl_errno($ch) . " " . curl_error($ch); | |
if (isDebug()) { | |
echo $url; | |
} | |
echo "</font><br>"; | |
} | |
curl_close($ch); | |
debug(print_r($page, true), "json"); | |
return $page; | |
} | } |
function curPageURL() | |
{ | function curPageURL() { |
$isHTTPS = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"); | $isHTTPS = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"); |
$port = (isset($_SERVER["SERVER_PORT"]) && ((!$isHTTPS && $_SERVER["SERVER_PORT"] != "80") || ($isHTTPS && $_SERVER["SERVER_PORT"] != "443"))); | $port = (isset($_SERVER["SERVER_PORT"]) && ((!$isHTTPS && $_SERVER["SERVER_PORT"] != "80") || ($isHTTPS && $_SERVER["SERVER_PORT"] != "443"))); |
$port = ($port) ? ':' . $_SERVER["SERVER_PORT"] : ''; | $port = ($port) ? ':' . $_SERVER["SERVER_PORT"] : ''; |
$url = ($isHTTPS ? 'https://' : 'http://') . $_SERVER["SERVER_NAME"] . $port . htmlentities(dirname($_SERVER['PHP_SELF']) , ENT_QUOTES); | $url = ($isHTTPS ? 'https://' : 'http://') . $_SERVER["SERVER_NAME"] . $port . htmlentities(dirname($_SERVER['PHP_SELF']), ENT_QUOTES); |
return $url; | return $url; |
} | } |
?> | ?> |
<?php | |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
if (isset($_REQUEST['firstLetter'])) { | |
$firstLetter = filter_var($_REQUEST['firstLetter'], FILTER_SANITIZE_STRING); | |
} | |
if (isset($_REQUEST['bysuburbs'])) { | |
$bysuburbs = true; | |
} | |
if (isset($_REQUEST['bynumber'])) { | |
$bynumber = true; | |
} | |
if (isset($_REQUEST['allstops'])) { | |
$allstops = true; | |
} | |
if (isset($_REQUEST['nearby'])) { | |
$nearby = true; | |
} | |
if (isset($_REQUEST['suburb'])) { | |
$suburb = $_REQUEST['suburb']; | |
} | |
if (isset($_REQUEST['pageKey'])) { | |
$pageKey = filter_var($_REQUEST['pageKey'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['lat'])) { | |
$lat = filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); | |
} | |
if (isset($_REQUEST['lon'])) { | |
$lon = filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION); | |
} | |
if (isset($_REQUEST['radius'])) { | |
$max_distance = filter_var($_REQUEST['radius'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['numberSeries'])) { | |
$numberSeries = filter_var($_REQUEST['numberSeries'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['routeDestination'])) { | |
$routeDestination = urldecode(filter_var($_REQUEST['routeDestination'], FILTER_SANITIZE_ENCODED)); | |
} | |
if (isset($_REQUEST['stopcode'])) { | |
$stopcode = filter_var($_REQUEST['stopcode'], FILTER_SANITIZE_STRING); | |
} | |
if (isset($_REQUEST['stopids'])) { | |
$stopids = explode(",", filter_var($_REQUEST['stopids'], FILTER_SANITIZE_STRING)); | |
} | |
if (isset($_REQUEST['tripid'])) { | |
$tripid = filter_var($_REQUEST['tripid'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['stopid'])) { | |
$stopid = filter_var($_REQUEST['stopid'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['routeid'])) { | |
$routeid = filter_var($_REQUEST['routeid'], FILTER_SANITIZE_NUMBER_INT); | |
} | |
if (isset($_REQUEST['geolocate'])) { | |
$geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); | |
} | |
?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
// you have to open the session to be able to modify or remove it | // you have to open the session to be able to modify or remove it |
session_start(); | session_start(); |
if (isset($_REQUEST['service_period'])) { | if (isset($_REQUEST['service_period'])) { |
$_SESSION['service_period'] = filter_var($_REQUEST['service_period'], FILTER_SANITIZE_STRING); | $_SESSION['service_period'] = filter_var($_REQUEST['service_period'], FILTER_SANITIZE_STRING); |
sessionUpdated(); | sessionUpdated(); |
} | } |
if (isset($_REQUEST['time'])) { | if (isset($_REQUEST['time'])) { |
$_SESSION['time'] = filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING); | $_SESSION['time'] = filter_var($_REQUEST['time'], FILTER_SANITIZE_STRING); |
sessionUpdated(); | sessionUpdated(); |
} | } |
if (isset($_REQUEST['geolocate']) && $_REQUEST['geolocate'] != "Enter co-ordinates or address here") { | if (isset($_REQUEST['geolocate']) && $_REQUEST['geolocate'] != "Enter co-ordinates or address here") { |
$geocoded = false; | $geocoded = false; |
if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) { | if (isset($_REQUEST['lat']) && isset($_REQUEST['lon'])) { |
$_SESSION['lat'] = trim(filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); | $_SESSION['lat'] = trim(filter_var($_REQUEST['lat'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); |
$_SESSION['lon'] = trim(filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); | $_SESSION['lon'] = trim(filter_var($_REQUEST['lon'], FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION)); |
} | } else { |
else { | if (startsWith($geolocate, "-")) { |
$geolocate = filter_var($_REQUEST['geolocate'], FILTER_SANITIZE_URL); | $locateparts = explode(",", $geolocate); |
if (startsWith($geolocate, "-")) { | $_SESSION['lat'] = $locateparts[0]; |
$locateparts = explode(",", $geolocate); | $_SESSION['lon'] = $locateparts[1]; |
$_SESSION['lat'] = $locateparts[0]; | } else if (strpos($geolocate, "(") !== false) { |
$_SESSION['lon'] = $locateparts[1]; | $geoParts = explode("(", $geolocate); |
} | $locateparts = explode(",", str_replace(")", "", $geoParts[1])); |
else { | $_SESSION['lat'] = $locateparts[0]; |
$contents = geocode($geolocate, true); | $_SESSION['lon'] = $locateparts[1]; |
print_r($contents); | } else { |
if (isset($contents[0]->centroid)) { | $contents = geocode($geolocate, true); |
$geocoded = true; | print_r($contents); |
$_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; | if (isset($contents[0]->centroid)) { |
$_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; | $geocoded = true; |
} | $_SESSION['lat'] = $contents[0]->centroid->coordinates[0]; |
else { | $_SESSION['lon'] = $contents[0]->centroid->coordinates[1]; |
$_SESSION['lat'] = ""; | } else { |
$_SESSION['lon'] = ""; | $_SESSION['lat'] = ""; |
} | $_SESSION['lon'] = ""; |
} | } |
} | } |
if ($_SESSION['lat'] != "" && isAnalyticsOn()) { | } |
trackEvent("Geolocation","Updated Location", "Geocoded - ".($geocoded ? "Yes" : "No")); | sessionUpdated(); |
} | |
sessionUpdated(); | |
} | } |
function sessionUpdated() { | function sessionUpdated() { |
$_SESSION['lastUpdated'] = time(); | $_SESSION['lastUpdated'] = time(); |
} | } |
// timeoutSession | // timeoutSession |
$TIMEOUT_LIMIT = 60*5; // 5 minutes | $TIMEOUT_LIMIT = 60 * 5; // 5 minutes |
if (isset($_SESSION['lastUpdated']) && $_SESSION['lastUpdated']+$TIMEOUT_LIMIT < time()) { | if (isset($_SESSION['lastUpdated']) && $_SESSION['lastUpdated'] + $TIMEOUT_LIMIT < time()) { |
debug ("Session timeout ".($_SESSION['lastUpdated']+$TIMEOUT_LIMIT).">".time(),"session"); | debug("Session timeout " . ($_SESSION['lastUpdated'] + $TIMEOUT_LIMIT) . ">" . time(), "session"); |
session_destroy(); | session_destroy(); |
session_start(); | session_start(); |
} | } |
//debug(print_r($_SESSION, true) , "session"); | //debug(print_r($_SESSION, true) , "session"); |
function current_time() { | |
return ($_SESSION['time'] ? $_SESSION['time'] : date("H:i:s")); | |
} | |
function current_time() { | |
return ($_SESSION['time']? $_SESSION['time'] : date("H:i:s")); | |
} | |
?> | ?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
// Copyright 2009 Google Inc. All Rights Reserved. | // Copyright 2009 Google Inc. All Rights Reserved. |
$GA_ACCOUNT = "MO-22173039-1"; | $GA_ACCOUNT = "MO-22173039-1"; |
$GA_PIXEL = "/lib/ga.php"; | $GA_PIXEL = "/lib/ga.php"; |
function googleAnalyticsGetImageUrl() | |
{ | function googleAnalyticsGetImageUrl() { |
global $GA_ACCOUNT, $GA_PIXEL; | global $GA_ACCOUNT, $GA_PIXEL; |
$url = ""; | //if (stristr($_SERVER['HTTP_USER_AGENT'], 'Googlebot') return ""; |
$url.= $GA_PIXEL . "?"; | $url = ""; |
$url.= "utmac=" . $GA_ACCOUNT; | $url.= $GA_PIXEL . "?"; |
$url.= "&utmn=" . rand(0, 0x7fffffff); | $url.= "utmac=" . $GA_ACCOUNT; |
$referer = $_SERVER["HTTP_REFERER"]; | $url.= "&utmn=" . rand(0, 0x7fffffff); |
$query = $_SERVER["QUERY_STRING"]; | $referer = $_SERVER["HTTP_REFERER"]; |
$path = $_SERVER["REQUEST_URI"]; | $query = $_SERVER["QUERY_STRING"]; |
if (empty($referer)) { | $path = $_SERVER["REQUEST_URI"]; |
$referer = "-"; | if (empty($referer)) { |
} | $referer = "-"; |
$url.= "&utmr=" . urlencode($referer); | } |
if (!empty($path)) { | $url.= "&utmr=" . urlencode($referer); |
$url.= "&utmp=" . urlencode($path); | if (!empty($path)) { |
} | $url.= "&utmp=" . urlencode($path); |
$url.= "&guid=ON"; | } |
return str_replace("&", "&", $url); | $url.= "&guid=ON"; |
} | return str_replace("&", "&", $url); |
function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false) | } |
{ | |
echo ' | function include_header($pageTitle, $pageType, $opendiv = true, $geolocate = false, $datepicker = false) { |
global $basePath, $GTFSREnabled; | |
echo ' | |
<!DOCTYPE html> | <!DOCTYPE html> |
<html lang="en"> | <html lang="en"> |
<head> | <head> |
<meta charset="UTF-8"> | <meta charset="UTF-8"> |
<title>' . $pageTitle . '</title> | <meta name="viewport" content="width=device-width, initial-scale=1"> |
<meta name="google-site-verification" | <title>' . $pageTitle . ' - Canberra Bus Timetable</title> |
content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" />'; | <meta name="google-site-verification" content="-53T5Qn4TB_de1NyfR_ZZkEVdUNcNFSaYKSFkWKx-sY" /> |
if ($datepicker) echo '<link rel="stylesheet" href="css/jquery.ui.datepicker.mobile.css" />'; | <link rel="dns-prefetch" href="//code.jquery.com"> |
if (isDebugServer()) { | <link rel="dns-prefetch" href="//ajax.googleapis.com"> |
echo '<link rel="stylesheet" href="css/jquery.mobile-1.0a4.css" /> | <link rel="stylesheet" href="' . $basePath . 'css/jquery-ui-1.8.12.custom.css" />'; |
$jqmVersion = "1.0rc1"; | |
<script type="text/javascript" src="js/jquery-1.5.js"></script> | if (isDebugServer()) { |
<script>$(document).bind("mobileinit", function(){ | $jqmcss = $basePath . "css/jquery.mobile-$jqmVersion.css"; |
$jqjs = $basePath . "js/jquery-1.6.2.min.js"; | |
$jqmjs = $basePath . "js/jquery.mobile-$jqmVersion.js"; | |
} else { | |
$jqmcss = "//code.jquery.com/mobile/$jqmVersion/jquery.mobile-$jqmVersion.min.css"; | |
$jqjs = "//ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"; | |
$jqmjs = "//code.jquery.com/mobile/$jqmVersion/jquery.mobile-$jqmVersion.min.js"; | |
} | |
echo '<link rel="stylesheet" href="' . $jqmcss . '" /> | |
<script src="' . $jqjs . '"></script> | |
<script>$(document).bind("mobileinit", function(){ | |
$.mobile.ajaxEnabled = false; | $.mobile.ajaxEnabled = false; |
}); | }); |
</script> | </script> |
<script type="text/javascript" src="js/jquery.mobile-1.0a4.js"></script>'; | <script src="' . $jqmjs . '"></script> |
} | |
else { | <script src="' . $basePath . 'js/jquery.ui.core.min.js"></script> |
echo '<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> | <script src="' . $basePath . 'js/jquery.ui.position.min.js"></script> |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script> | <script src="' . $basePath . 'js/jquery.ui.widget.min.js"></script> |
<script>$(document).bind("mobileinit", function(){ | <script src="' . $basePath . 'js/jquery.ui.autocomplete.min.js"></script> |
$.mobile.ajaxEnabled = false; | <script> |
}); | $(function() { |
</script> | $( "#geolocate" ).autocomplete({ |
<script type="text/javascript" src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script>'; | source: "lib/autocomplete.php", |
} | minLength: 2 |
if ($datepicker) { | }); |
echo '<script> | $( "#from" ).autocomplete({ |
//reset type=date inputs to text | source: "lib/autocomplete.php", |
$( document ).bind( "mobileinit", function(){ | minLength: 2 |
$.mobile.page.prototype.options.degradeInputs.date = true; | }); |
}); | $( "#to" ).autocomplete({ |
</script> | source: "lib/autocomplete.php", |
<script src="js/jQuery.ui.datepicker.js"></script>'; | minLength: 2 |
} | }); |
echo '<style type="text/css"> | }); |
.ui-navbar { | </script>'; |
width: 100%; | echo '<style type="text/css">'; |
} | if (strstr($_SERVER['HTTP_USER_AGENT'], 'Android')) |
.ui-btn-inner { | echo '.ui-shadow,.ui-btn-up-a,.ui-btn-hover-a,.ui-btn-down-a,.ui-body-b,.ui-btn-up-b,.ui-btn-hover-b, |
white-space: normal !important; | .ui-btn-down-b,.ui-bar-c,.ui-body-c,.ui-btn-up-c,.ui-btn-hover-c,.ui-btn-down-c,.ui-bar-c,.ui-body-d, |
} | .ui-btn-up-d,.ui-btn-hover-d,.ui-btn-down-d,.ui-bar-d,.ui-body-e,.ui-btn-up-e,.ui-btn-hover-e, |
.ui-li-heading { | .ui-btn-down-e,.ui-bar-e,.ui-overlay-shadow,.ui-shadow,.ui-btn-active,.ui-body-a,.ui-bar-a { |
white-space: normal !important; | text-shadow: none; |
} | box-shadow: none; |
.ui-listview-filter { | -webkit-box-shadow: none; |
margin: 0 !important; | }'; |
} | echo '</style>'; |
.ui-icon-navigation { | echo '<link rel="stylesheet" href="' . $basePath . 'css/local.css.php" />'; |
background-image: url(css/images/113-navigation.png); | if (isIOSDevice()){ |
background-position: 1px 0; | echo '<meta name="apple-mobile-web-app-capable" content="yes" /> |
} | |
.ui-icon-beaker { | |
background-image: url(css/images/91-beaker-2.png); | |
background-position: 1px 0; | |
} | |
#footer { | |
text-size: 0.75em; | |
text-align: center; | |
} | |
body { | |
background-color: #F0F0F0; | |
} | |
#jqm-homeheader { | |
text-align: center; | |
} | |
.viaPoints { | |
display: none; | |
text-size: 0.2em; | |
} | |
.min-width-480px .viaPoints { | |
display: inline; | |
} | |
#extrainfo { | |
visibility: hidden; | |
display: none; | |
} | |
#servicewarning { | |
padding: 1em; | |
margin-bottom: 0.5em; | |
text-size: 0.2em; | |
background-color: #FF9; | |
-moz-border-radius: 15px; | |
border-radius: 15px; | |
} | |
// source http://webaim.org/techniques/skipnav/ | |
#skip a, #skip a:hover, #skip a:visited | |
{ | |
position:absolute; | |
left:0px; | |
top:-500px; | |
width:1px; | |
height:1px; | |
overflow:hidden; | |
} | |
#skip a:active, #skip a:focus | |
{ | |
position:static; | |
width:auto; | |
height:auto; | |
} | |
</style>'; | |
if (strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod')) { | |
echo '<meta name="apple-mobile-web-app-capable" content="yes" /> | |
<meta name="apple-mobile-web-app-status-bar-style" content="black" /> | <meta name="apple-mobile-web-app-status-bar-style" content="black" /> |
<link rel="apple-touch-startup-image" href="startup.png" /> | <link rel="apple-touch-startup-image" href="startup.png" /> |
<link rel="apple-touch-icon" href="apple-touch-icon.png" />'; | <link rel="apple-touch-icon" href="apple-touch-icon.png" />'; |
} | } |
if ($geolocate) { | if ($geolocate) { |
echo "<script> | echo "<script> |
function success(position) { | function success(position) { |
$('#error').val('Location now detected. Please wait for data to load.'); | $('#error').val('Location now detected. Please wait for data to load.'); |
$('#geolocate').val(position.coords.latitude+','+position.coords.longitude); | $('#geolocate').val(position.coords.latitude+','+position.coords.longitude); |
$.ajax({ url: \"include/common.inc.php?geolocate=yes&lat=\"+position.coords.latitude+\"&lon=\"+position.coords.longitude }); | $.ajax({ async: false, |
location.reload(true); | success: function(){ |
location.reload(true); | |
}, | |
url: \"include/common.inc.php?geolocate=yes&lat=\"+position.coords.latitude+\"&lon=\"+position.coords.longitude }); | |
} | } |
function error(msg) { | function error(msg) { |
$('#error').val('Error: '+msg); | $('#error').val('Error: '+msg); |
} | } |
function geolocate() { | function geolocate() { |
if (navigator.geolocation) { | if (navigator.geolocation) { |
var options = { | var options = { |
enableHighAccuracy: true, | enableHighAccuracy: true, |
timeout: 60000, | timeout: 60000, |
maximumAge: 10000 | maximumAge: 10000 |
} | } |
navigator.geolocation.getCurrentPosition(success, error, options); | navigator.geolocation.getCurrentPosition(success, error, options); |
} | } |
} | } |
$(document).ready(function() { | $(document).ready(function() { |
$('#here').click(function(event) { $('#geolocate').val(geolocate()); return false;}); | $('#here').click(function(event) { $('#geolocate').val(geolocate()); return false;}); |
$('#here').show(); | $('#here').show(); |
}); | }); |
"; | "; |
if (!isset($_SESSION['lat']) || $_SESSION['lat'] == "") echo "geolocate();"; | if (!isset($_SESSION['lat']) || $_SESSION['lat'] == "") |
echo "</script> "; | echo "geolocate();"; |
} | echo "</script> "; |
if (isAnalyticsOn()) echo ' | } |
if (isAnalyticsOn()) | |
echo ' | |
<script type="text/javascript">' . " | <script type="text/javascript">' . " |
var _gaq = _gaq || []; | var _gaq = _gaq || []; |
_gaq.push(['_setAccount', 'UA-22173039-1']); | _gaq.push(['_setAccount', 'UA-22173039-1']); |
_gaq.push(['_trackPageview']); | _gaq.push(['_trackPageview']); |
_gaq.push(['_trackPageLoadTime']); | |
</script>"; | </script>"; |
echo '</head> | echo '</head> |
<body> | <body> |
<div id="skip"> | <div id="skip"> |
<a href="#maincontent">Skip to content</a> | <a href="#maincontent">Skip to content</a> |
</div> | </div> |
'; | '; |
if ($opendiv) { | if ($opendiv) { |
echo '<div data-role="page"> | echo '<div data-role="page"> |
<div data-role="header" data-position="inline"> | <div data-role="header" data-position="inline"> |
<a href="' . (isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "javascript:history.go(-1)") . '" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a> | <a href="' . (isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "javascript:history.go(-1)") . '" data-icon="arrow-l" data-rel="back" class="ui-btn-left">Back</a> |
<h1>' . $pageTitle . '</h1> | <h1>' . $pageTitle . '</h1> |
<a href="/index.php" data-icon="home" class="ui-btn-right">Home</a> | <a href="' . $basePath . '/index.php" data-icon="home" class="ui-btn-right">Home</a> |
</div><!-- /header --> | </div><!-- /header --> |
<a name="maincontent" id="maincontent"></a> | <a name="maincontent" id="maincontent"></a> |
<div data-role="content"> '; | <div data-role="content"> '; |
$overrides = getServiceOverride(); | $overrides = getServiceOverride(); |
if ($overrides['service_id']) { | if ($overrides['service_id']) { |
if ($overrides['service_id'] == "noservice") { | if ($overrides['service_id'] == "noservice") { |
echo '<div id="servicewarning">Buses are <strong>not running today</strong> due to industrial action/public holiday. See <a | echo '<div id="servicewarning">Buses are <strong>not running today</strong> due to industrial action/public holiday. See <a |
href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>'; | href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>'; |
} | } else { |
else { | echo '<div id="servicewarning">Buses are running on an altered timetable today due to industrial action/public holiday. See <a href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>'; |
echo '<div id="servicewarning">Buses are running on an altered timetable today due to industrial action/public holiday. See <a href="http://www.action.act.gov.au">http://www.action.act.gov.au</a> for details.</div>'; | } |
} | } |
} | if ($GTFSREnabled) { |
} | $serviceAlerts = getServiceAlertsAsArray("agency", "0"); |
if (isset($serviceAlerts['entity']) && sizeof($serviceAlerts['entity']) > 0) { | |
} | foreach ($serviceAlerts['entity'] as $entity) { |
function include_footer() | echo "<div id='servicewarning'>" . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['start'])) . " to " . date("F j, g:i a", strtotime($entity['alert']['active_period'][0]['end'])) . "{$entity['alert']['header_text']['translation'][0]['text']}<br>Warning: {$entity['alert']['description_text']['translation'][0]['text']} |
{ | <br><a href='{$entity['alert']['url']['translation'][0]['text']}'>Source</a> </div>"; |
echo '<div id="footer"><a href="about.php">About/Contact Us</a> <a href="feedback.php">Feedback/Bug Report</a>'; | } |
echo '</div>'; | } |
if (isAnalyticsOn()) { | } |
echo "<script> (function() { | } |
} | |
function include_footer() { | |
global $basePath; | |
echo '<div id="footer"><a href="' . $basePath . 'about.php">About/Contact Us</a> <a href="' . $basePath . 'feedback.php">Feedback/Bug Report</a> <a href="' . $basePath . 'privacy.php">Privacy Policy</a>'; | |
echo '</div>'; | |
if (isAnalyticsOn()) { | |
echo "<script> (function() { | |
var ga = document.createElement('script'); ga.type = | var ga = document.createElement('script'); ga.type = |
'text/javascript'; ga.async = true; | 'text/javascript'; ga.async = true; |
ga.src = ('https:' == document.location.protocol ? | ga.src = ('https:' == document.location.protocol ? |
'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; |
var s = document.getElementsByTagName('script')[0]; | var s = document.getElementsByTagName('script')[0]; |
s.parentNode.insertBefore(ga, s); | s.parentNode.insertBefore(ga, s); |
})();</script>"; | })();</script>"; |
$googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(); | $googleAnalyticsImageUrl = googleAnalyticsGetImageUrl(); |
echo '<noscript><img src="' . $googleAnalyticsImageUrl . '" /></noscript>'; | echo '<noscript><img src="' . $googleAnalyticsImageUrl . '" /></noscript>'; |
} | |
} | echo "\n</div></div></body></html>"; |
echo "\n</div></div></body></html>"; | } |
} | |
function timePlaceSettings($geolocate = false) | function placeSettings() { |
{ | global $service_periods; |
global $service_periods; | $geoerror = false; |
$geoerror = false; | $geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == ""; |
if ($geolocate == true) { | |
$geoerror = !isset($_SESSION['lat']) || !isset($_SESSION['lat']) || $_SESSION['lat'] == "" || $_SESSION['lon'] == ""; | echo '<div id="error">'; |
} | if ($geoerror) { |
if ($geoerror) { | echo 'Sorry, but your location could not currently be detected. |
echo '<div id="error">Sorry, but your location could not currently be detected. | |
Please allow location permission, wait for your location to be detected, | Please allow location permission, wait for your location to be detected, |
or enter an address/co-ordinates in the box below.</div>'; | or enter an address/co-ordinates in the box below.'; |
} | } |
echo '<div data-role="collapsible" data-collapsed="' . !$geoerror . '"> | echo '</div>'; |
<h3>Change Time/Place (' . (isset($_SESSION['time']) ? $_SESSION['time'] : "Current Time,") . ' ' . ucwords(service_period()) . ')...</h3> | echo '<div id="settings" data-role="collapsible" data-collapsed="' . !$geoerror . '"> |
<h3>Change Location...</h3> | |
<form action="' . basename($_SERVER['PHP_SELF']) . "?" . $_SERVER['QUERY_STRING'] . '" method="post"> | <form action="' . basename($_SERVER['PHP_SELF']) . "?" . $_SERVER['QUERY_STRING'] . '" method="post"> |
<div class="ui-body"> | <div class="ui-body"> |
<div data-role="fieldcontain"> | <div data-role="fieldcontain"> |
<label for="geolocate"> Current Location: </label> | <label for="geolocate"> Current Location: </label> |
<input type="text" id="geolocate" name="geolocate" value="' . (isset($_SESSION['lat']) && isset($_SESSION['lon']) ? $_SESSION['lat'] . "," . $_SESSION['lon'] : "Enter co-ordinates or address here") . '"/> <a href="#" style="display:none" name="here" id="here">Here?</a> | <input type="text" id="geolocate" name="geolocate" value="' . (isset($_SESSION['lat']) && isset($_SESSION['lon']) ? $_SESSION['lat'] . "," . $_SESSION['lon'] : "Enter co-ordinates or address here") . '"/> <a href="#" style="display:none" name="here" id="here">Here?</a> |
</div> | </div> |
<div data-role="fieldcontain"> | |
<label for="time"> Time: </label> | |
<input type="time" name="time" id="time" value="' . (isset($_SESSION['time']) ? $_SESSION['time'] : date("H:i")) . '"/> | |
<a href="#" name="currentTime" id="currentTime" onClick="var d = new Date();' . "$('#time').val(d.getHours() +':'+ (d.getMinutes().toString().length == 1 ? '0'+ d.getMinutes(): d.getMinutes()));" . '">Current Time?</a> | |
</div> | |
<div data-role="fieldcontain"> | |
<label for="service_period"> Service Period: </label> | |
<select name="service_period" id="service_period">'; | |
foreach ($service_periods as $service_period) { | |
echo "<option value=\"$service_period\"" . (service_period() === $service_period ? " SELECTED" : "") . '>' . ucwords($service_period) . '</option>'; | |
} | |
echo '</select> | |
<a href="#" style="display:none" name="currentPeriod" id="currentPeriod">Current Period?</a> | |
</div> | |
<input type="submit" value="Update"/> | <input type="submit" value="Update"/> |
</div></form> | </div></form> |
</div>'; | </div>'; |
} | } |
function trackEvent($category, $action, $label = "", $value = - 1) | |
{ | function trackEvent($category, $action, $label = "", $value = - 1) { |
if (isAnalyticsOn()) { | if (isAnalyticsOn()) { |
echo "\n<script> _gaq.push(['_trackEvent', '$category', '$action'" . ($label != "" ? ", '$label'" : "") . ($value != - 1 ? ", $value" : "") . "]);</script>"; | echo "\n<script> _gaq.push(['_trackEvent', '$category', '$action'" . ($label != "" ? ", '$label'" : "") . ($value != - 1 ? ", $value" : "") . "]);</script>"; |
} | } |
} | } |
?> | ?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
$service_periods = Array( | $service_periods = Array( |
'sunday', | 'sunday', |
'saturday', | 'saturday', |
'weekday' | 'weekday' |
); | ); |
function getServiceOverride() { | |
global $conn; | function service_period($date = "") { |
$query = "Select * from calendar_dates where date = '".date("Ymd")."' and exception_type = '1'"; | |
debug($query,"database"); | if (isset($_SESSION['service_period'])) |
$result = pg_query($conn, $query); | return $_SESSION['service_period']; |
if (!$result) { | $override = getServiceOverride($date); |
databaseError(pg_result_error($result)); | if ($override['service_id']) { |
return Array(); | return $override['service_id']; |
} | } |
return pg_fetch_assoc($result); | |
} | switch (date('w', ($date != "" ? $date : time()))) { |
function service_period() | case 0: |
{ | return 'sunday'; |
case 6: | |
if (isset($_SESSION['service_period'])) return $_SESSION['service_period']; | return 'saturday'; |
$override = getServiceOverride(); | default: |
if ($override['service_id']){ | return 'weekday'; |
return $override['service_id']; | } |
} | } |
switch (date('w')) { | function midnight_seconds($time = "") { |
case 0: | // from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html |
return 'sunday'; | if ($time != "") { |
case 6: | return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time); |
return 'saturday'; | } |
default: | if (isset($_SESSION['time'])) { |
return 'weekday'; | $time = strtotime($_SESSION['time']); |
} | return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time); |
} | } |
function midnight_seconds() | return (date("G") * 3600) + (date("i") * 60) + date("s"); |
{ | } |
// from http://www.perturb.org/display/Perlfunc__Seconds_Since_Midnight.html | |
if (isset($_SESSION['time'])) { | function midnight_seconds_to_time($seconds) { |
$time = strtotime($_SESSION['time']); | if ($seconds > 0) { |
return (date("G", $time) * 3600) + (date("i", $time) * 60) + date("s", $time); | $midnight = mktime(0, 0, 0, date("n"), date("j"), date("Y")); |
} | return date("h:ia", $midnight + $seconds); |
return (date("G") * 3600) + (date("i") * 60) + date("s"); | } else { |
} | return ""; |
function midnight_seconds_to_time($seconds) | } |
{ | } |
if ($seconds > 0) { | |
$midnight = mktime(0, 0, 0, date("n") , date("j") , date("Y")); | if ($GTFSREnabled) { |
return date("h:ia", $midnight + $seconds); | $serviceAlertCause = Array( |
} | "UNKNOWN_CAUSE" => "Unknown cause", |
else { | "OTHER_CAUSE" => "Other cause", |
return ""; | "TECHNICAL_PROBLEM" => "Technical problem", |
} | "STRIKE" => "Strike", |
"DEMONSTRATION" => "Demonstration", | |
"ACCIDENT" => "Accident", | |
"HOLIDAY" => "Holiday", | |
"WEATHER" => "Weather", | |
"MAINTENANCE" => "Maintenance", | |
"CONSTRUCTION" => "Construction", | |
"POLICE_ACTIVITY" => "Police activity", | |
"MEDICAL_EMERGENCY" => "Medical emergency" | |
); | |
$serviceAlertEffect = Array( | |
"NO_SERVICE" => "No service", | |
"REDUCED_SERVICE" => "Reduced service", | |
"SIGNIFICANT_DELAYS" => "Significant delays", | |
"DETOUR" => "Detour", | |
"ADDITIONAL_SERVICE" => "Additional service", | |
"MODIFIED_SERVICE" => "Modified service", | |
"OTHER_EFFECT" => "Other effect", | |
"UNKNOWN_EFFECT" => "Unknown effect", | |
"STOP_MOVED" => "Stop moved"); | |
set_include_path(get_include_path() . PATH_SEPARATOR . ($basePath . "lib/Protobuf-PHP/library/DrSlump/")); | |
include_once("Protobuf.php"); | |
include_once("Protobuf/Message.php"); | |
include_once("Protobuf/Registry.php"); | |
include_once("Protobuf/Descriptor.php"); | |
include_once("Protobuf/Field.php"); | |
include_once($basePath . "lib/Protobuf-PHP/gtfs-realtime.php"); | |
include_once("Protobuf/CodecInterface.php"); | |
include_once("Protobuf/Codec/PhpArray.php"); | |
include_once("Protobuf/Codec/Binary.php"); | |
include_once("Protobuf/Codec/Binary/Writer.php"); | |
include_once("Protobuf/Codec/Json.php"); | |
function getServiceAlerts($filter_class = "", $filter_id = "") { | |
/* | |
also need last modified epoch of client gtfs | |
- add,remove,patch,inform (null) | |
- stop | |
- trip | |
- network | |
- classes (WHERE=) | |
- route (short_name or route_id) | |
- street | |
- stop | |
- trip | |
Currently support: | |
network inform | |
trip patch: stop remove | |
street inform: route inform, trip inform, stop inform | |
route patch: trip remove | |
*/ | |
$fm = new transit_realtime\FeedMessage(); | |
$fh = new transit_realtime\FeedHeader(); | |
$fh->setGtfsRealtimeVersion(1); | |
$fh->setTimestamp(time()); | |
$fm->setHeader($fh); | |
foreach (getCurrentAlerts() as $alert) { | |
$fe = new transit_realtime\FeedEntity(); | |
$fe->setId($alert['id']); | |
$fe->setIsDeleted(false); | |
$alert = new transit_realtime\Alert(); | |
$tr = new transit_realtime\TimeRange(); | |
$tr->setStart($alert['start']); | |
$tr->setEnd($alert['end']); | |
$alert->addActivePeriod($tr); | |
$informedEntities = getInformedAlerts($alert['id'], $_REQUEST['filter_class'], $_REQUEST['filter_id']); | |
if (sizeof($informedEntities) > 0) { | |
$informed = Array(); | |
$es = new transit_realtime\EntitySelector(); | |
if ($informedEntity['informed_class'] == "agency") { | |
$es->setAgencyId($informedEntity['informed_id']); | |
} | |
if ($informedEntity['informed_class'] == "stop") { | |
$es->setStopId($informedEntity['informed_id']); | |
} | |
if ($informedEntity['informed_class'] == "route") { | |
$es->setRouteId($informedEntity['informed_id']); | |
} | |
if ($informedEntity['informed_class'] == "trip") { | |
$td = new transit_realtime\TripDescriptor(); | |
$td->setTripId($informedEntity['informed_id']); | |
$es->setTrip($td); | |
} | |
$alert->addInformedEntity($es); | |
} | |
$alert->setCause(constant("transit_realtime\Alert\Cause::" . $alert['cause'])); | |
$alert->setEffect(constant("transit_realtime\Alert\Effect::" . $alert['effect'])); | |
$tsUrl = new transit_realtime\TranslatedString(); | |
$tUrl = new transit_realtime\TranslatedString\Translation(); | |
$tUrl->setText($alert['url']); | |
$tUrl->setLanguage("en"); | |
$tsUrl->addTranslation($tUrl); | |
$alert->setUrl($tsUrl); | |
$tsHeaderText = new transit_realtime\TranslatedString(); | |
$tHeaderText = new transit_realtime\TranslatedString\Translation(); | |
$tHeaderText->setText($alert['header']); | |
$tHeaderText->setLanguage("en"); | |
$tsHeaderText->addTranslation($tHeaderText); | |
$alert->setHeaderText($tsHeaderText); | |
$tsDescriptionText = new transit_realtime\TranslatedString(); | |
$tDescriptionText = new transit_realtime\TranslatedString\Translation(); | |
$tDescriptionText->setText($alert['description']); | |
$tDescriptionText->setLanguage("en"); | |
$tsDescriptionText->addTranslation($tDescriptionText); | |
$alert->setDescriptionText($tsDescriptionText); | |
$fe->setAlert($alert); | |
$fm->addEntity($fe); | |
} | |
return $fm; | |
} | |
function getServiceAlertsAsArray($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\PhpArray(); | |
return $codec->encode(getServiceAlerts($filter_class, $filter_id)); | |
} | |
function getServiceAlertsAsBinary($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\Binary(); | |
return $codec->encode(getServiceAlerts($filter_class, $filter_id)); | |
} | |
function getServiceAlertsAsJSON($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\Json(); | |
return $codec->encode(getServiceAlerts($filter_class, $filter_id)); | |
} | |
function getServiceAlertsByClass() { | |
$return = Array(); | |
$alerts = getServiceAlertsAsArray("", ""); | |
foreach ($alerts['entities'] as $entity) { | |
foreach ($entity['informed'] as $informed) { | |
foreach ($informed as $key => $value) { | |
if (strpos("_id", $key) > 0) { | |
$parts = explode($key); | |
$class = $parts[0]; | |
$id = $value; | |
} | |
} | |
$return[$class][$id][] = $entity; | |
} | |
} | |
} | |
function getTripUpdates($filter_class = "", $filter_id = "") { | |
$fm = new transit_realtime\FeedMessage(); | |
$fh = new transit_realtime\FeedHeader(); | |
$fh->setGtfsRealtimeVersion(1); | |
$fh->setTimestamp(time()); | |
$fm->setHeader($fh); | |
foreach (getCurrentAlerts() as $alert) { | |
$informedEntities = getInformedAlerts($alert['id'], $_REQUEST['filter_class'], $_REQUEST['filter_id']); | |
$stops = Array(); | |
$routestrips = Array(); | |
if (sizeof($informedEntities) > 0) { | |
if ($informedEntity['informed_class'] == "stop" && $informed["x-action"] == "remove") { | |
$stops[] = $informedEntity['informed_id']; | |
} | |
if (($informedEntity['informed_class'] == "route" || $informedEntity['informed_class'] == "trip") && $informed["x-action"] == "patch") { | |
$routestrips[] = Array("id" => $informedEntity['informed_id'], | |
"type" => $informedEntity['informed_class']); | |
} | |
} | |
foreach ($routestrips as $routetrip) { | |
$fe = new transit_realtime\FeedEntity(); | |
$fe->setId($alert['id'] . $routetrip['id']); | |
$fe->setIsDeleted(false); | |
$tu = new transit_realtime\TripUpdate(); | |
$td = new transit_realtime\TripDescriptor(); | |
if ($routetrip['type'] == "route") { | |
$td->setRouteId($routetrip['id']); | |
} else if ($routetrip['type'] == "trip") { | |
$td->setTripId($routetrip['id']); | |
} | |
$tu->setTrip($td); | |
foreach ($stops as $stop) { | |
$stu = new transit_realtime\TripUpdate\StopTimeUpdate(); | |
$stu->setStopId($stop); | |
$stu->setScheduleRelationship(transit_realtime\TripUpdate\StopTimeUpdate\ScheduleRelationship::SKIPPED); | |
$tu->addStopTimeUpdate($stu); | |
} | |
$fe->setTripUpdate($tu); | |
$fm->addEntity($fe); | |
} | |
} | |
return $fm; | |
} | |
function getTripUpdatesAsArray($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\PhpArray(); | |
return $codec->encode(getTripUpdates($filter_class, $filter_id)); | |
} | |
function getTripUpdatesAsBinary($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\Binary(); | |
return $codec->encode(getTripUpdates($filter_class, $filter_id)); | |
} | |
function getTripUpdatesAsJSON($filter_class = "", $filter_id = "") { | |
$codec = new DrSlump\Protobuf\Codec\Json(); | |
return $codec->encode(getTripUpdates($filter_class, $filter_id)); | |
} | |
} | } |
?> | ?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
date_default_timezone_set('Australia/ACT'); | date_default_timezone_set('Australia/ACT'); |
$debugOkay = Array( | $debugOkay = Array( |
"session", | "session", |
"json", | "json", |
"phperror", | "phperror", |
"awsotp", | "awsotp", |
//"squallotp", | //"squallotp", |
"vanilleotp", | //"vanilleotp", |
"database", | "database", |
"other" | "other" |
); | ); |
$GTFSREnabled = true; | |
$cloudmadeAPIkey = "daa03470bb8740298d4b10e3f03d63e6"; | $cloudmadeAPIkey = "daa03470bb8740298d4b10e3f03d63e6"; |
$googleMapsAPIkey = "ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q"; | $googleMapsAPIkey = "ABQIAAAA95XYXN0cki3Yj_Sb71CFvBTPaLd08ONybQDjcH_VdYtHHLgZvRTw2INzI_m17_IoOUqH3RNNmlTk1Q"; |
$otpAPIurl = 'http://localhost:8080/opentripplanner-api-webapp/'; | $otpAPIurl = 'http://localhost:8080/opentripplanner-api-webapp/'; |
if (isDebug("awsotp") || php_uname('n') == "maxious.xen.prgmr.com") { | if (isDebug("awsotp") || php_uname('n') == "maxious.xen.prgmr.com") { |
$otpAPIurl = 'http://bus-main.lambdacomplex.org:8080/opentripplanner-api-webapp/'; | $otpAPIurl = 'http://bus-main.lambdacomplex.org:8080/opentripplanner-api-webapp/'; |
} | } |
if (isDebug("dotcloudotp") || php_uname('n') == "actbus-www") { | if (isDebug("dotcloudotp") || php_uname('n') == "actbus-www") { |
$otpAPIurl = 'http://otp.actbus.dotcloud.com/opentripplanner-api-webapp/'; | $otpAPIurl = 'http://otp.actbus.dotcloud.com/opentripplanner-api-webapp/'; |
} | } |
if (isDebug("squallotp")) { | if (isDebug("squallotp")) { |
$otpAPIurl = 'http://10.0.1.108:5080/opentripplanner-api-webapp/'; | $otpAPIurl = 'http://10.0.1.108:5080/opentripplanner-api-webapp/'; |
} | } |
if (isDebug("vanilleotp")) { | if (isDebug("vanilleotp")) { |
$otpAPIurl = 'http://10.0.1.135:8080/opentripplanner-api-webapp/'; | $otpAPIurl = 'http://10.0.1.135:8080/opentripplanner-api-webapp/'; |
} | } |
if (isDebug("phperror")) error_reporting(E_ALL ^ E_NOTICE); | if (isDebug("phperror")) |
error_reporting(E_ALL ^ E_NOTICE); | |
$basePath = ""; | |
if (strstr($_SERVER['PHP_SELF'], "labs/") | |
|| strstr($_SERVER['PHP_SELF'], "myway/") | |
|| strstr($_SERVER['PHP_SELF'], "lib/") | |
|| strstr($_SERVER['PHP_SELF'], "geo/") | |
|| strstr($_SERVER['PHP_SELF'], "include/") | |
|| strstr($_SERVER['PHP_SELF'], "servicealerts/")) | |
$basePath = "../"; | |
function isDebugServer() { | |
return php_sapi_name() == "cli" || isset($_SERVER['SERVER_NAME']) && ( $_SERVER['SERVER_NAME'] == "azusa" || $_SERVER['SERVER_NAME'] == "vanille" | |
|| $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1"); | |
} | |
include_once ("common-geo.inc.php"); | include_once ("common-geo.inc.php"); |
include_once ("common-net.inc.php"); | include_once ("common-net.inc.php"); |
include_once ("common-transit.inc.php"); | include_once ("common-transit.inc.php"); |
include_once ("common-db.inc.php"); | |
include_once ("common-request.inc.php"); | |
include_once ("common-session.inc.php"); | include_once ("common-session.inc.php"); |
include_once ("common-db.inc.php"); | include_once ("common-auth.inc.php"); |
include_once ("common-template.inc.php"); | include_once ("common-template.inc.php"); |
include_once ("common-request.inc.php"); | |
function isAnalyticsOn() { | |
function isDebugServer() | $user_agent = $_SERVER['HTTP_USER_AGENT']; |
{ | return!isDebugServer() && !preg_match('/cloudkick/i', $user_agent) && !preg_match('/googlebot/i', $user_agent) && |
return $_SERVER['SERVER_NAME'] == "10.0.1.154" || $_SERVER['SERVER_NAME'] == "localhost" || $_SERVER['SERVER_NAME'] == "127.0.0.1" || !$_SERVER['SERVER_NAME']; | !preg_match('/baidu/i', $user_agent); |
} | } |
function isAnalyticsOn() | |
{ | function isDebug($debugReason = "other") { |
return !isDebugServer(); | global $debugOkay; |
} | return in_array($debugReason, $debugOkay, false) && isDebugServer(); |
function isDebug($debugReason = "other") | } |
{ | |
global $debugOkay; | function debug($msg, $debugReason = "other") { |
return in_array($debugReason, $debugOkay, false) && isDebugServer(); | if (isDebug($debugReason)) |
} | echo "\n<!-- " . date(DATE_RFC822) . "\n $msg -->\n"; |
function debug($msg, $debugReason = "other") | } |
{ | function isIOSDevice() { |
if (isDebug($debugReason)) echo "\n<!-- " . date(DATE_RFC822) . "\n $msg -->\n"; | return strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPod') || strstr($_SERVER['HTTP_USER_AGENT'], 'iPad'); |
} | } |
function isJQueryMobileDevice() | function isJQueryMobileDevice() { |
{ | // http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897 |
// http://forum.jquery.com/topic/what-is-the-best-way-to-detect-all-useragents-which-can-handle-jquery-mobile#14737000002087897 | $user_agent = $_SERVER['HTTP_USER_AGENT']; |
$user_agent = $_SERVER['HTTP_USER_AGENT']; | return preg_match('/iphone/i', $user_agent) || preg_match('/android/i', $user_agent) || preg_match('/webos/i', $user_agent) || preg_match('/ios/i', $user_agent) || preg_match('/bada/i', $user_agent) || preg_match('/maemo/i', $user_agent) || preg_match('/meego/i', $user_agent) || preg_match('/fennec/i', $user_agent) || (preg_match('/symbian/i', $user_agent) && preg_match('/s60/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/symbian/i', $user_agent) && preg_match('/platform/i', $user_agent) && $browser['majorver'] >= 3) || (preg_match('/blackberry/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/opera mobile/i', $user_agent) && $browser['majorver'] >= 10) || (preg_match('/opera mini/i', $user_agent) && $browser['majorver'] >= 5); |
return preg_match('/iphone/i', $user_agent) || preg_match('/android/i', $user_agent) || preg_match('/webos/i', $user_agent) || preg_match('/ios/i', $user_agent) || preg_match('/bada/i', $user_agent) || preg_match('/maemo/i', $user_agent) || preg_match('/meego/i', $user_agent) || preg_match('/fennec/i', $user_agent) || (preg_match('/symbian/i', $user_agent) && preg_match('/s60/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/symbian/i', $user_agent) && preg_match('/platform/i', $user_agent) && $browser['majorver'] >= 3) || (preg_match('/blackberry/i', $user_agent) && $browser['majorver'] >= 5) || (preg_match('/opera mobile/i', $user_agent) && $browser['majorver'] >= 10) || (preg_match('/opera mini/i', $user_agent) && $browser['majorver'] >= 5); | } |
} | |
function isFastDevice() | |
{ | function array_flatten($a, $f = array()) { |
$ua = $_SERVER['HTTP_USER_AGENT']; | if (!$a || !is_array($a)) |
$fastDevices = Array( | return ''; |
"Mozilla/5.0 (X11;", | foreach ($a as $k => $v) { |
"Mozilla/5.0 (Windows;", | if (is_array($v)) |
"Mozilla/5.0 (iP", | $f = array_flatten($v, $f); |
"Mozilla/5.0 (Linux; U; Android", | else |
"Mozilla/4.0 (compatible; MSIE" | $f[$k] = $v; |
); | } |
$slowDevices = Array( | return $f; |
"J2ME", | } |
"MIDP", | |
"Opera/", | function remove_spaces($string) { |
"Mozilla/2.0 (compatible;", | return str_replace(' ', '', $string); |
"Mozilla/3.0 (compatible;" | } |
); | |
return true; | function object2array($object) { |
} | if (is_object($object)) { |
function array_flatten($a, $f = array()) | foreach ($object as $key => $value) { |
{ | $array[$key] = $value; |
if (!$a || !is_array($a)) return ''; | } |
foreach ($a as $k => $v) { | } else { |
if (is_array($v)) $f = array_flatten($v, $f); | $array = $object; |
else $f[$k] = $v; | } |
} | return $array; |
return $f; | } |
} | |
function remove_spaces($string) | function startsWith($haystack, $needle, $case = true) { |
{ | if ($case) { |
return str_replace(' ', '', $string); | return (strcmp(substr($haystack, 0, strlen($needle)), $needle) === 0); |
} | } |
function object2array($object) | return (strcasecmp(substr($haystack, 0, strlen($needle)), $needle) === 0); |
{ | } |
if (is_object($object)) { | |
foreach ($object as $key => $value) { | function endsWith($haystack, $needle, $case = true) { |
$array[$key] = $value; | if ($case) { |
} | return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0); |
} | } |
else { | return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0); |
$array = $object; | } |
} | |
return $array; | function bracketsMeanNewLine($input) { |
} | return str_replace(")", "</small>", str_replace("(", "<br><small>", $input)); |
function startsWith($haystack, $needle, $case = true) | } |
{ | |
if ($case) { | function sksort(&$array, $subkey = "id", $sort_ascending = false) { |
return (strcmp(substr($haystack, 0, strlen($needle)) , $needle) === 0); | if (count($array)) |
} | $temp_array[key($array)] = array_shift($array); |
return (strcasecmp(substr($haystack, 0, strlen($needle)) , $needle) === 0); | foreach ($array as $key => $val) { |
} | $offset = 0; |
$found = false; | |
function endsWith($haystack, $needle, $case = true) | foreach ($temp_array as $tmp_key => $tmp_val) { |
{ | if (!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) { |
if ($case) { | $temp_array = array_merge((array) array_slice($temp_array, 0, $offset), array( |
return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0); | $key => $val |
} | ), array_slice($temp_array, $offset)); |
return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)) , $needle) === 0); | $found = true; |
} | } |
function bracketsMeanNewLine($input) | $offset++; |
{ | } |
return str_replace(")", "</small>", str_replace("(", "<br><small>", $input)); | if (!$found) |
} | $temp_array = array_merge($temp_array, array( |
function sksort(&$array, $subkey = "id", $sort_ascending = false) | $key => $val |
{ | )); |
if (count($array)) $temp_array[key($array) ] = array_shift($array); | } |
foreach ($array as $key => $val) { | if ($sort_ascending) |
$offset = 0; | $array = array_reverse($temp_array); |
$found = false; | else |
foreach ($temp_array as $tmp_key => $tmp_val) { | $array = $temp_array; |
if (!$found and strtolower($val[$subkey]) > strtolower($tmp_val[$subkey])) { | } |
$temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array( | |
$key => $val | function sktimesort(&$array, $subkey = "id", $sort_ascending = false) { |
) , array_slice($temp_array, $offset)); | if (count($array)) |
$found = true; | $temp_array[key($array)] = array_shift($array); |
} | foreach ($array as $key => $val) { |
$offset++; | $offset = 0; |
} | $found = false; |
if (!$found) $temp_array = array_merge($temp_array, array( | foreach ($temp_array as $tmp_key => $tmp_val) { |
$key => $val | if (!$found and strtotime($val[$subkey]) > strtotime($tmp_val[$subkey])) { |
)); | $temp_array = array_merge((array) array_slice($temp_array, 0, $offset), array( |
} | $key => $val |
if ($sort_ascending) $array = array_reverse($temp_array); | ), array_slice($temp_array, $offset)); |
else $array = $temp_array; | $found = true; |
} | } |
function sktimesort(&$array, $subkey = "id", $sort_ascending = false) | $offset++; |
{ | } |
if (count($array)) $temp_array[key($array) ] = array_shift($array); | if (!$found) |
foreach ($array as $key => $val) { | $temp_array = array_merge($temp_array, array( |
$offset = 0; | $key => $val |
$found = false; | )); |
foreach ($temp_array as $tmp_key => $tmp_val) { | } |
if (!$found and strtotime($val[$subkey]) > strtotime($tmp_val[$subkey])) { | if ($sort_ascending && isset($temp_array)) |
$temp_array = array_merge((array)array_slice($temp_array, 0, $offset) , array( | $array = array_reverse($temp_array); |
$key => $val | else |
) , array_slice($temp_array, $offset)); | $array = $temp_array; |
$found = true; | } |
} | |
$offset++; | function r_implode($glue, $pieces) { |
} | foreach ($pieces as $r_pieces) { |
if (!$found) $temp_array = array_merge($temp_array, array( | if (is_array($r_pieces)) { |
$key => $val | $retVal[] = r_implode($glue, $r_pieces); |
)); | } else { |
} | $retVal[] = $r_pieces; |
if ($sort_ascending) $array = array_reverse($temp_array); | } |
else $array = $temp_array; | } |
} | return implode($glue, $retVal); |
function r_implode( $glue, $pieces ) | } |
{ | |
foreach( $pieces as $r_pieces ) | |
{ | |
if( is_array( $r_pieces ) ) | |
{ | |
$retVal[] = r_implode( $glue, $r_pieces ); | |
} | |
else | |
{ | |
$retVal[] = $r_pieces; | |
} | |
} | |
return implode( $glue, $retVal ); | |
} | |
?> | ?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
function getRoute($routeID) { | function getRoute($routeID) { |
global $conn; | global $conn; |
$query = "Select * from routes where route_id = '$routeID' LIMIT 1"; | $query = "Select * from routes where route_id = :routeID LIMIT 1"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":routeID", $routeID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
return pg_fetch_assoc($result); | return Array(); |
} | } |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getRouteByFullName($routeFullName) { | |
global $conn; | |
$query = "Select * from routes where route_short_name||route_long_name = :routeFullName LIMIT 1"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":routeFullName", $routeFullName); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getRoutes() { | function getRoutes() { |
global $conn; | global $conn; |
$query = "Select * from routes order by route_short_name;"; | $query = "Select * from routes order by route_short_name;"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->execute(); |
databaseError(pg_result_error($result)); | if (!$query) { |
return Array(); | databaseError($conn->errorInfo()); |
} | return Array(); |
return pg_fetch_all($result); | } |
return $query->fetchAll(); | |
} | } |
function getRoutesByNumber($routeNumber = "") { | function getRoutesByNumber($routeNumber = "") { |
global $conn; | global $conn; |
if ($routeNumber != "") { | if ($routeNumber != "") { |
$query = "Select distinct routes.route_id,routes.route_short_name,routes.route_long_name,service_id from routes join trips on trips.route_id = | $query = "Select distinct routes.route_id,routes.route_short_name,routes.route_long_name,service_id from routes join trips on trips.route_id = |
routes.route_id join stop_times on stop_times.trip_id = trips.trip_id where route_short_name = '$routeNumber' order by route_short_name;"; | routes.route_id join stop_times on stop_times.trip_id = trips.trip_id |
} else { | where route_short_name = :routeNumber OR route_short_name LIKE :routeNumber2 order by route_short_name;"; |
$query = "SELECT DISTINCT route_short_name from routes order by route_short_name"; | } else { |
} | $query = "SELECT DISTINCT route_short_name from routes order by route_short_name"; |
debug($query,"database"); | } |
$result = pg_query($conn, $query); | debug($query, "database"); |
if (!$result) { | $query = $conn->prepare($query); |
databaseError(pg_result_error($result)); | if ($routeNumber != "") { |
return Array(); | $query->bindParam(":routeNumber", $routeNumber); |
} | $routeNumber2 = "% " . $routeNumber; |
return pg_fetch_all($result); | $query->bindParam(":routeNumber2", $routeNumber2); |
} | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getRoutesByNumberSeries($routeNumberSeries = "") { | |
global $conn; | |
if (strlen($routeNumberSeries) == 1) { | |
return getRoutesByNumber($routeNumberSeries); | |
} | |
$seriesMin = substr($routeNumberSeries, 0, -1) . "0"; | |
$seriesMax = substr($routeNumberSeries, 0, -1) . "9"; | |
$query = "Select distinct routes.route_id,routes.route_short_name,routes.route_long_name,service_id from routes join trips on trips.route_id = | |
routes.route_id join stop_times on stop_times.trip_id = trips.trip_id where to_number(route_short_name, 'FM999') between :seriesMin and :seriesMax OR route_short_name LIKE :routeNumberSeries order by route_short_name;"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":seriesMin", $seriesMin); | |
$query->bindParam(":seriesMax", $seriesMax); | |
$routeNumberSeries = "% " . substr($routeNumberSeries, 0, -1) . "%"; | |
$query->bindParam(":routeNumberSeries", $routeNumberSeries); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | } |
function getRouteNextTrip($routeID) { | function getRouteNextTrip($routeID) { |
global $conn; | global $conn; |
$query = "select * from routes join trips on trips.route_id = routes.route_id | $query = "select * from routes join trips on trips.route_id = routes.route_id |
join stop_times on stop_times.trip_id = trips.trip_id where | join stop_times on stop_times.trip_id = trips.trip_id where |
arrival_time > '".current_time()."' and routes.route_id = '$routeID' order by | arrival_time > :currentTime and routes.route_id = :routeID order by |
arrival_time limit 1"; | arrival_time limit 1"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":currentTime", current_time()); |
databaseError(pg_result_error($result)); | $query->bindParam(":routeID", $routeID); |
return Array(); | $query->execute(); |
} | if (!$query) { |
$r = pg_fetch_assoc($result); | databaseError($conn->errorInfo()); |
// past last trip of the day special case | return Array(); |
if (sizeof($r) == 0) { | } |
$query = "select * from routes join trips on trips.route_id = routes.route_id | $r = $query->fetch(PDO :: FETCH_ASSOC); |
join stop_times on stop_times.trip_id = trips.trip_id where routes.route_id = '$routeID' order by | |
// past last trip of the day special case | |
if (sizeof($r) < 16) { | |
$query = "select * from routes join trips on trips.route_id = routes.route_id | |
join stop_times on stop_times.trip_id = trips.trip_id where routes.route_id = :routeID order by | |
arrival_time DESC limit 1"; | arrival_time DESC limit 1"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":routeID", $routeID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
$r = pg_fetch_assoc($result); | return Array(); |
} | } |
return $r; | |
} | $r = $query->fetch(PDO :: FETCH_ASSOC); |
} | |
function getTimeInterpolatedRouteAtStop($routeID, $stop_id) | return $r; |
{ | } |
function getTimeInterpolatedRouteAtStop($routeID, $stop_id) { | |
$nextTrip = getRouteNextTrip($routeID); | $nextTrip = getRouteNextTrip($routeID); |
if ($nextTrip['trip_id']){ | if ($nextTrip['trip_id']) { |
foreach (getTimeInterpolatedTrip($nextTrip['trip_id']) as $tripStop) { | foreach (getTimeInterpolatedTrip($nextTrip['trip_id']) as $tripStop) { |
if ($tripStop['stop_id'] == $stop_id) return $tripStop; | if ($tripStop['stop_id'] == $stop_id) |
} | return $tripStop; |
} | } |
return Array(); | } |
} | return Array(); |
} | |
function getRouteTrips($routeID) { | function getRouteTrips($routeID) { |
global $conn; | global $conn; |
$query = "select routes.route_id,trips.trip_id,service_id,arrival_time, stop_id, stop_sequence from routes join trips on trips.route_id = routes.route_id | $query = "select routes.route_id,trips.trip_id,service_id,arrival_time, stop_id, stop_sequence from routes join trips on trips.route_id = routes.route_id |
join stop_times on stop_times.trip_id = trips.trip_id where routes.route_id = '$routeID' and stop_sequence = '1' order by | join stop_times on stop_times.trip_id = trips.trip_id where routes.route_id = :routeID and stop_sequence = '1' order by |
arrival_time "; | arrival_time "; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":routeID", $routeID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
return pg_fetch_all($result); | return Array(); |
} | } |
return $query->fetchAll(); | |
} | |
function getRoutesByDestination($destination = "", $service_period = "") { | function getRoutesByDestination($destination = "", $service_period = "") { |
global $conn; | global $conn; |
if ($service_period == "") $service_period = service_period(); | if ($service_period == "") |
if ($destination != "") { | $service_period = service_period(); |
$query = "SELECT DISTINCT trips.route_id,route_short_name,route_long_name, service_id | if ($destination != "") { |
$query = "SELECT DISTINCT trips.route_id,route_short_name,route_long_name, service_id | |
FROM stop_times join trips on trips.trip_id = | FROM stop_times join trips on trips.trip_id = |
stop_times.trip_id join routes on trips.route_id = routes.route_id | stop_times.trip_id join routes on trips.route_id = routes.route_id |
WHERE route_long_name = '$destination' AND service_id='$service_period' order by route_short_name"; | WHERE route_long_name = :destination AND service_id=:service_period order by route_short_name"; |
} else { | } else { |
$query = "SELECT DISTINCT route_long_name | $query = "SELECT DISTINCT route_long_name |
FROM stop_times join trips on trips.trip_id = | FROM stop_times join trips on trips.trip_id = |
stop_times.trip_id join routes on trips.route_id = routes.route_id | stop_times.trip_id join routes on trips.route_id = routes.route_id |
WHERE service_id='$service_period' order by route_long_name"; | WHERE service_id= :service_period order by route_long_name"; |
} | } |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":service_period", $service_period); |
databaseError(pg_result_error($result)); | if ($destination != "") |
return Array(); | $query->bindParam(":destination", $destination); |
} | $query->execute(); |
return pg_fetch_all($result); | if (!$query) { |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | } |
function getRoutesBySuburb($suburb, $service_period = "") { | function getRoutesBySuburb($suburb, $service_period = "") { |
if ($service_period == "") $service_period = service_period(); | if ($service_period == "") |
global $conn; | $service_period = service_period(); |
$query = "SELECT DISTINCT service_id,trips.route_id,route_short_name,route_long_name | global $conn; |
$query = "SELECT DISTINCT service_id,trips.route_id,route_short_name,route_long_name | |
FROM stop_times join trips on trips.trip_id = stop_times.trip_id | FROM stop_times join trips on trips.trip_id = stop_times.trip_id |
join routes on trips.route_id = routes.route_id | join routes on trips.route_id = routes.route_id |
join stops on stops.stop_id = stop_times.stop_id | join stops on stops.stop_id = stop_times.stop_id |
WHERE zone_id LIKE '%$suburb;%' AND service_id='$service_period' ORDER BY route_short_name"; | WHERE zone_id LIKE ':suburb AND service_id=:service_period ORDER BY route_short_name"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":service_period", $service_period); |
databaseError(pg_result_error($result)); | $suburb = "%" . $suburb . ";%"; |
return Array(); | $query->bindParam(":suburb", $suburb); |
} | $query->execute(); |
return pg_fetch_all($result); | if (!$query) { |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | } |
function getRoutesNearby($lat, $lng, $limit = "", $distance = 500) { | function getRoutesNearby($lat, $lng, $limit = "", $distance = 500) { |
if ($service_period == "") | |
$service_period = service_period(); | |
if ($service_period == "") $service_period = service_period(); | if ($limit != "") |
if ($limit != "") $limit = " LIMIT $limit "; | $limitSQL = " LIMIT :limit "; |
global $conn; | global $conn; |
$query = "SELECT service_id,trips.route_id,route_short_name,route_long_name,min(stops.stop_id) as stop_id, | $query = "SELECT service_id,trips.route_id,route_short_name,route_long_name,min(stops.stop_id) as stop_id, |
min(ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE)) as distance | min(ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE)) as distance |
FROM stop_times | FROM stop_times |
join trips on trips.trip_id = stop_times.trip_id | join trips on trips.trip_id = stop_times.trip_id |
join routes on trips.route_id = routes.route_id | join routes on trips.route_id = routes.route_id |
join stops on stops.stop_id = stop_times.stop_id | join stops on stops.stop_id = stop_times.stop_id |
WHERE service_id='$service_period' | WHERE service_id=:service_period |
AND ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), $distance, FALSE) | AND ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), :distance, FALSE) |
group by service_id,trips.route_id,route_short_name,route_long_name | group by service_id,trips.route_id,route_short_name,route_long_name |
order by distance $limit"; | order by distance $limitSQL"; |
debug($query,"database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":service_period", $service_period); |
databaseError(pg_result_error($result)); | $query->bindParam(":distance", $distance); |
return Array(); | if ($limit != "") |
} | $query->bindParam(":limit", $limit); |
return pg_fetch_all($result); | $query->execute(); |
} | if (!$query) { |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
?> | ?> |
<?php | |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
function getServiceOverride($date = "") { | |
global $conn; | |
$query = "Select * from calendar_dates where date = :date and exception_type = '1' LIMIT 1"; | |
// debug($query,"database"); | |
$query = $conn->prepare($query); // Create a prepared statement | |
$query->bindParam(":date", date("Ymd", ($date != "" ? $date : time()))); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getServiceAlert($alertID) { | |
global $conn; | |
$query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where id = :servicealert_id"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":servicealert_id", $alertID); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function updateServiceAlert($alertID, $start, $end, $header, $description, $url) { | |
global $conn; | |
$query = 'update servicealerts_alerts set start=:start, "end"=:end, header=:header, description=:description, url=:url where id = :servicealert_id'; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":servicealert_id", $alertID); | |
$query->bindParam(":start", $start); | |
$query->bindParam(":end", $end); | |
$query->bindParam(":header", $header); | |
$query->bindParam(":description", $description); | |
$query->bindParam(":url", $url); | |
$query->execute(); | |
print_r($conn->errorInfo()); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function addServiceAlert($start, $end, $header, $description, $url) { | |
global $conn; | |
$query = 'INSERT INTO servicealerts_alerts (start, "end", header, description, url) VALUES (:start, :end, :header, :description, :url) '; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":start", $start); | |
$query->bindParam(":end", $end); | |
$query->bindParam(":header", $header); | |
$query->bindParam(":description", $description); | |
$query->bindParam(":url", $url); | |
$query->execute(); | |
print_r($conn->errorInfo()); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getCurrentAlerts() { | |
global $conn; | |
$query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start and NOW() < \"end\""; | |
// debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getFutureAlerts() { | |
global $conn; | |
$query = "SELECT id,extract('epoch' from start) as start, extract('epoch' from end) as end,cause,effect,header,description,url from servicealerts_alerts where NOW() > start or NOW() < \"end\""; | |
// debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getInformedAlerts($id, $filter_class, $filter_id) { | |
global $conn; | |
$query = "SELECT * from servicealerts_informed where servicealert_id = :servicealert_id"; | |
if ($filter_class != "") { | |
$query .= " AND informed_class = :informed_class "; | |
} | |
if ($filter_id != "") { | |
$query .= " AND informed_id = :informed_id "; | |
} | |
// debug($query, "database"); | |
$query = $conn->prepare($query); | |
if ($filter_class != "") { | |
$query->bindParam(":informed_class", $filter_class); | |
} | |
if ($filter_id != "") { | |
$query->bindParam(":informed_id", $filter_id); | |
} | |
$query->bindParam(":servicealert_id", $id); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function deleteInformedAlert($serviceAlertID, $class, $id) { | |
global $conn; | |
$query = 'DELETE from servicealerts_informed where servicealert_id = :servicealert_id and informed_class = :informed_class AND informed_id = :informed_id'; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":servicealert_id", $serviceAlertID); | |
$query->bindParam(":informed_class", $class); | |
$query->bindParam(":informed_id", $id); | |
$query->execute(); | |
print_r($conn->errorInfo()); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return null; | |
} | |
function addInformedAlert($serviceAlertID, $class, $id, $action) { | |
global $conn; | |
$query = 'INSERT INTO servicealerts_informed (servicealert_id , informed_class , informed_id) VALUES(:servicealert_id ,:informed_class, :informed_id)'; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":servicealert_id", $serviceAlertID); | |
$query->bindParam(":informed_class", $class); | |
$query->bindParam(":informed_id", $id); | |
$query->execute(); | |
print_r($conn->errorInfo()); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return null; | |
} | |
?> |
<?php | <?php |
function getStop($stopID) | |
{ | /* |
global $conn; | * Copyright 2010,2011 Alexander Sadleir |
$query = "Select * from stops where stop_id = '$stopID' LIMIT 1"; | |
debug($query, "database"); | Licensed under the Apache License, Version 2.0 (the "License"); |
$result = pg_query($conn, $query); | you may not use this file except in compliance with the License. |
if (!$result) { | You may obtain a copy of the License at |
databaseError(pg_result_error($result)); | |
return Array(); | http://www.apache.org/licenses/LICENSE-2.0 |
} | |
return pg_fetch_assoc($result); | Unless required by applicable law or agreed to in writing, software |
} | distributed under the License is distributed on an "AS IS" BASIS, |
function getStops($timingPointsOnly = false, $firstLetter = "") | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
{ | See the License for the specific language governing permissions and |
global $conn; | limitations under the License. |
$conditions = Array(); | */ |
if ($timingPointsOnly) $conditions[] = "substr(stop_code,1,2) != 'Wj'"; | |
if ($firstLetter != "") $conditions[] = "substr(stop_name,1,1) = '$firstLetter'"; | function getStop($stopID) { |
$query = "Select * from stops"; | global $conn; |
if (sizeof($conditions) > 0) { | $query = "Select * from stops where stop_id = :stopID LIMIT 1"; |
if (sizeof($conditions) > 1) { | debug($query, "database"); |
$query.= " Where " . implode(" AND ", $conditions) . " "; | $query = $conn->prepare($query); |
} | $query->bindParam(":stopID", $stopID); |
else { | $query->execute(); |
$query.= " Where " . $conditions[0] . " "; | if (!$query) { |
} | databaseError($conn->errorInfo()); |
} | return Array(); |
$query.= " order by stop_name;"; | } |
debug($query, "database"); | return $query->fetch(PDO :: FETCH_ASSOC); |
$result = pg_query($conn, $query); | } |
if (!$result) { | |
databaseError(pg_result_error($result)); | function getStops($timingPointsOnly = false, $firstLetter = "", $startsWith = "") { |
return Array(); | global $conn; |
} | $conditions = Array(); |
return pg_fetch_all($result); | if ($timingPointsOnly) |
} | $conditions[] = "substr(stop_code,1,2) != 'Wj'"; |
function getNearbyStops($lat, $lng, $limit = "", $distance = 1000) | if ($firstLetter != "") |
{ | $conditions[] = "substr(stop_name,1,1) = :firstLetter"; |
if ($lat == null || $lng == null) return Array(); | if ($startsWith != "") |
if ($limit != "") $limit = " LIMIT $limit "; | $conditions[] = "stop_name like :startsWith"; |
global $conn; | $query = "Select * from stops"; |
$query = "Select *, ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE) as distance | if (sizeof($conditions) > 0) { |
from stops WHERE ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), $distance, FALSE) | if (sizeof($conditions) > 1) { |
order by distance $limit;"; | $query .= " Where " . implode(" AND ", $conditions) . " "; |
debug($query, "database"); | } else { |
$result = pg_query($conn, $query); | $query .= " Where " . $conditions[0] . " "; |
if (!$result) { | } |
databaseError(pg_result_error($result)); | } |
return Array(); | $query .= " order by stop_name;"; |
} | $query = $conn->prepare($query); |
return pg_fetch_all($result); | if ($firstLetter != "") |
} | $query->bindParam(":firstLetter", $firstLetter); |
function getStopsBySuburb($suburb) | |
{ | if ($startsWith != "") { |
global $conn; | $startsWith = $startsWith . "%"; |
$query = "Select * from stops where zone_id LIKE '%$suburb;%' order by stop_name;"; | $query->bindParam(":startsWith", $startsWith); |
debug($query, "database"); | } |
$result = pg_query($conn, $query); | $query->execute(); |
if (!$result) { | if (!$query) { |
databaseError(pg_result_error($result)); | databaseError($conn->errorInfo()); |
return Array(); | return Array(); |
} | } |
return pg_fetch_all($result); | return $query->fetchAll(); |
} | } |
function getStopRoutes($stopID, $service_period) | |
{ | function getNearbyStops($lat, $lng, $limit = "", $distance = 1000) { |
if ($service_period == "") $service_period = service_period(); | if ($lat == null || $lng == null) |
global $conn; | return Array(); |
$query = "SELECT service_id,trips.route_id,route_short_name,route_long_name | if ($limit != "") |
$limitSQL = " LIMIT :limit "; | |
global $conn; | |
$query = "Select *, ST_Distance(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), FALSE) as distance | |
from stops WHERE ST_DWithin(position, ST_GeographyFromText('SRID=4326;POINT($lng $lat)'), :distance, FALSE) | |
order by distance $limitSQL;"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":distance", $distance); | |
$query->bindParam(":limit", $limit); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getStopsByName($name) { | |
global $conn; | |
$query = "Select * from stops where stop_name LIKE :name;"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$name = "%" . $name . ";%"; | |
$query->bindParam(":name", $name); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getStopsBySuburb($suburb) { | |
global $conn; | |
$query = "Select * from stops where zone_id LIKE :suburb order by stop_name;"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$suburb = "%" . $suburb . ";%"; | |
$query->bindParam(":suburb", $suburb); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getStopsByStopCode($stop_code, $startsWith = "") { | |
global $conn; | |
$query = "Select * from stops where (stop_code = :stop_code OR stop_code LIKE :stop_code2)"; | |
if ($startsWith != "") | |
$query .= " AND stop_name like :startsWith"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":stop_code", $stop_code); | |
$stop_code2 = $stop_code . "%"; | |
$query->bindParam(":stop_code2", $stop_code2); | |
if ($startsWith != "") { | |
$startsWith = $startsWith . "%"; | |
$query->bindParam(":startsWith", $startsWith); | |
} | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetchAll(); | |
} | |
function getStopRoutes($stopID, $service_period) { | |
if ($service_period == "") | |
$service_period = service_period(); | |
global $conn; | |
$query = "SELECT distinct service_id,trips.route_id,route_short_name,route_long_name | |
FROM stop_times join trips on trips.trip_id = | FROM stop_times join trips on trips.trip_id = |
stop_times.trip_id join routes on trips.route_id = routes.route_id WHERE stop_id = '$stopID' AND service_id='$service_period'"; | stop_times.trip_id join routes on trips.route_id = routes.route_id WHERE stop_id = :stopID AND service_id=:service_period"; |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":service_period", $service_period); |
databaseError(pg_result_error($result)); | $query->bindParam(":stopID", $stopID); |
return Array(); | $query->execute(); |
} | if (!$query) { |
return pg_fetch_all($result); | databaseError($conn->errorInfo()); |
} | return Array(); |
function getStopTrips($stopID, $service_period = "", $afterTime = "") | } |
{ | return $query->fetchAll(); |
if ($service_period == "") $service_period = service_period(); | } |
$afterCondition = "AND arrival_time > '$afterTime'"; | |
global $conn; | function getStopTrips($stopID, $service_period = "", $afterTime = "", $limit = "") { |
if ($afterTime != "") { | if ($service_period == "") |
$query = " SELECT stop_times.trip_id,stop_times.arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name, end_times.arrival_time as end_time | $service_period = service_period(); |
if ($limit != "") | |
$limitSQL = " LIMIT :limit "; | |
global $conn; | |
if ($afterTime != "") { | |
$query = " SELECT stop_times.trip_id,stop_times.arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name, end_times.arrival_time as end_time | |
FROM stop_times | FROM stop_times |
join trips on trips.trip_id = | join trips on trips.trip_id = |
stop_times.trip_id | stop_times.trip_id |
join routes on trips.route_id = routes.route_id , (SELECT trip_id,max(arrival_time) as arrival_time from stop_times | join routes on trips.route_id = routes.route_id , (SELECT trip_id,max(arrival_time) as arrival_time from stop_times |
WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times | WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times |
WHERE stop_times.stop_id = '$stopID' | WHERE stop_times.stop_id = :stopID |
AND stop_times.trip_id = end_times.trip_id | AND stop_times.trip_id = end_times.trip_id |
AND service_id='$service_period' | AND service_id=:service_period |
AND end_times.arrival_time > '$afterTime' | AND end_times.arrival_time > :afterTime |
ORDER BY end_time"; | ORDER BY end_time $limitSQL"; |
} | } else { |
else { | $query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name |
$query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_sequence,service_id,trips.route_id,route_short_name,route_long_name | |
FROM stop_times | FROM stop_times |
join trips on trips.trip_id = | join trips on trips.trip_id = |
stop_times.trip_id | stop_times.trip_id |
join routes on trips.route_id = routes.route_id | join routes on trips.route_id = routes.route_id |
WHERE stop_times.stop_id = '$stopID' | WHERE stop_times.stop_id = :stopID |
AND service_id='$service_period' | AND service_id=:service_period |
ORDER BY arrival_time"; | ORDER BY arrival_time $limitSQL"; |
} | } |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":service_period", $service_period); |
databaseError(pg_result_error($result)); | $query->bindParam(":stopID", $stopID); |
return Array(); | if ($limit != "") |
} | $query->bindParam(":limit", $limit); |
return pg_fetch_all($result); | if ($afterTime != "") |
} | $query->bindParam(":afterTime", $afterTime); |
function getStopTripsWithTimes($stopID, $time = "", $service_period = "", $time_range = "", $limit = "") | $query->execute(); |
{ | if (!$query) { |
if ($service_period == "") $service_period = service_period(); | databaseError($conn->errorInfo()); |
if ($time_range == "") $time_range = (24 * 60 * 60); | return Array(); |
if ($time == "") $time = current_time(); | } |
if ($limit == "") $limit = 10; | return $query->fetchAll(); |
$trips = getStopTrips($stopID, $service_period, $time); | } |
$timedTrips = Array(); | |
if ($trips && sizeof($trips) > 0) { | function getStopTripsWithTimes($stopID, $time = "", $service_period = "", $time_range = "", $limit = "") { |
foreach ($trips as $trip) { | if ($service_period == "") |
if ($trip['arrival_time'] != "") { | $service_period = service_period(); |
if (strtotime($trip['arrival_time']) > strtotime($time) and strtotime($trip['arrival_time']) < (strtotime($time) + $time_range)) { | if ($time_range == "") |
$timedTrips[] = $trip; | $time_range = (24 * 60 * 60); |
} | if ($time == "") |
} | $time = current_time(); |
else { | if ($limit == "") |
$timedTrip = getTimeInterpolatedTripAtStop($trip['trip_id'], $trip['stop_sequence']); | $limit = 10; |
if ($timedTrip['arrival_time'] > $time and strtotime($timedTrip['arrival_time']) < (strtotime($time) + $time_range)) { | $trips = getStopTrips($stopID, $service_period, $time); |
$timedTrips[] = $timedTrip; | $timedTrips = Array(); |
} | if ($trips && sizeof($trips) > 0) { |
} | foreach ($trips as $trip) { |
if (sizeof($timedTrips) > $limit) break; | if ($trip['arrival_time'] != "") { |
} | if (strtotime($trip['arrival_time']) > strtotime($time) and strtotime($trip['arrival_time']) < (strtotime($time) + $time_range)) { |
sktimesort($timedTrips, "arrival_time", true); | $timedTrips[] = $trip; |
} | |
} else { | |
$timedTrip = getTimeInterpolatedTripAtStop($trip['trip_id'], $trip['stop_sequence']); | |
if ($timedTrip['arrival_time'] > $time and strtotime($timedTrip['arrival_time']) < (strtotime($time) + $time_range)) { | |
$timedTrips[] = $timedTrip; | |
} | |
} | |
if (sizeof($timedTrips) > $limit) | |
break; | |
} | } |
return $timedTrips; | sktimesort($timedTrips, "arrival_time", true); |
} | } |
return $timedTrips; | |
} | |
?> | ?> |
<?php | <?php |
function getTrip($tripID) | |
{ | /* |
global $conn; | * Copyright 2010,2011 Alexander Sadleir |
$query = "Select * from trips | |
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. | |
*/ | |
function getTrip($tripID) { | |
global $conn; | |
$query = "Select * from trips | |
join routes on trips.route_id = routes.route_id | join routes on trips.route_id = routes.route_id |
where trip_id = '$tripID' | where trip_id = :tripID |
LIMIT 1"; | LIMIT 1"; |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":tripID", $tripID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
return pg_fetch_assoc($result); | return Array(); |
} | } |
function getTripShape() | return $query->fetch(PDO :: FETCH_ASSOC); |
{ | } |
/* def handle_json_GET_tripstopTimes(self, params): | |
schedule = self.server.schedule | function getTripShape($tripID) { |
try: | global $conn; |
trip = schedule.GetTrip(params.get('trip')) | $query = "SELECT ST_AsKML(ST_MakeLine(geometry(a.position))) as the_route |
except KeyError: | FROM (SELECT position, |
# if a non-existent trip is searched for, the return nothing | stop_sequence, trips.trip_id |
return | FROM stop_times |
time_stops = trip.GetTimeInterpolatedStops() | join trips on trips.trip_id = stop_times.trip_id |
stops = [] | join stops on stops.stop_id = stop_times.stop_id |
times = [] | WHERE trips.trip_id = :tripID ORDER BY stop_sequence) as a group by a.trip_id"; |
for arr,ts,is_timingpoint in time_stops: | debug($query, "database"); |
stops.append(StopToTuple(ts.stop)) | $query = $conn->prepare($query); |
times.append(arr) | $query->bindParam(":tripID", $tripID); |
return [stops, times] | $query->execute(); |
if (!$query) { | |
def handle_json_GET_tripshape(self, params): | databaseError($conn->errorInfo()); |
schedule = self.server.schedule | return Array(); |
try: | } |
trip = schedule.GetTrip(params.get('trip')) | return $query->fetchColumn(0); |
except KeyError: | } |
# if a non-existent trip is searched for, the return nothing | |
return | function getTimeInterpolatedTrip($tripID, $range = "") { |
points = [] | global $conn; |
if trip.shape_id: | $query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_lat,stop_lon,stop_name,stop_code, |
shape = schedule.GetShape(trip.shape_id) | |
for (lat, lon, dist) in shape.points: | |
points.append((lat, lon)) | |
else: | |
time_stops = trip.GetTimeStops() | |
for arr,dep,stop in time_stops: | |
points.append((stop.stop_lat, stop.stop_lon)) | |
return points*/ | |
} | |
function getTimeInterpolatedTrip($tripID, $range = "") | |
{ | |
global $conn; | |
$query = "SELECT stop_times.trip_id,arrival_time,stop_times.stop_id,stop_lat,stop_lon,stop_name,stop_code, | |
stop_sequence,service_id,trips.route_id,route_short_name,route_long_name | stop_sequence,service_id,trips.route_id,route_short_name,route_long_name |
FROM stop_times | FROM stop_times |
join trips on trips.trip_id = stop_times.trip_id | join trips on trips.trip_id = stop_times.trip_id |
join routes on trips.route_id = routes.route_id | join routes on trips.route_id = routes.route_id |
join stops on stops.stop_id = stop_times.stop_id | join stops on stops.stop_id = stop_times.stop_id |
WHERE trips.trip_id = '$tripID' $range ORDER BY stop_sequence"; | WHERE trips.trip_id = :tripID $range ORDER BY stop_sequence"; |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":tripID", $tripID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
$stopTimes = pg_fetch_all($result); | return Array(); |
$cur_timepoint = Array(); | } |
$next_timepoint = Array(); | $stopTimes = $query->fetchAll(); |
$distance_between_timepoints = 0.0; | $cur_timepoint = Array(); |
$distance_traveled_between_timepoints = 0.0; | $next_timepoint = Array(); |
$rv = Array(); | $distance_between_timepoints = 0.0; |
foreach ($stopTimes as $i => $stopTime) { | $distance_traveled_between_timepoints = 0.0; |
if ($stopTime['arrival_time'] != "") { | $rv = Array(); |
// is timepoint | foreach ($stopTimes as $i => $stopTime) { |
$cur_timepoint = $stopTime; | if ($stopTime['arrival_time'] != "") { |
$distance_between_timepoints = 0.0; | // is timepoint |
$distance_traveled_between_timepoints = 0.0; | $cur_timepoint = $stopTime; |
if ($i + 1 < sizeof($stopTimes)) { | $distance_between_timepoints = 0.0; |
$k = $i + 1; | $distance_traveled_between_timepoints = 0.0; |
$distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]); | if ($i + 1 < sizeof($stopTimes)) { |
while ($stopTimes[$k]["arrival_time"] == "" && $k + 1 < sizeof($stopTimes)) { | $k = $i + 1; |
$k += 1; | $distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]); |
//echo "k".$k; | while ($stopTimes[$k]["arrival_time"] == "" && $k + 1 < sizeof($stopTimes)) { |
$distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]); | $k += 1; |
} | // echo "k".$k; |
$next_timepoint = $stopTimes[$k]; | $distance_between_timepoints += distance($stopTimes[$k - 1]["stop_lat"], $stopTimes[$k - 1]["stop_lon"], $stopTimes[$k]["stop_lat"], $stopTimes[$k]["stop_lon"]); |
$rv[] = $stopTime; | } |
} | $next_timepoint = $stopTimes[$k]; |
} | } |
else { | $rv[] = $stopTime; |
// is untimed point | } else { |
//echo "i".$i; | // is untimed point |
$distance_traveled_between_timepoints += distance($stopTimes[$i - 1]["stop_lat"], $stopTimes[$i - 1]["stop_lon"], $stopTimes[$i]["stop_lat"], $stopTimes[$i]["stop_lon"]); | // echo "i".$i; |
//echo "$distance_traveled_between_timepoints / $distance_between_timepoints<br>"; | $distance_traveled_between_timepoints += distance($stopTimes[$i - 1]["stop_lat"], $stopTimes[$i - 1]["stop_lon"], $stopTimes[$i]["stop_lat"], $stopTimes[$i]["stop_lon"]); |
$distance_percent = $distance_traveled_between_timepoints / $distance_between_timepoints; | // echo "$distance_traveled_between_timepoints / $distance_between_timepoints<br>"; |
if ($next_timepoint["arrival_time"] != "") { | $distance_percent = $distance_traveled_between_timepoints / $distance_between_timepoints; |
$total_time = strtotime($next_timepoint["arrival_time"]) - strtotime($cur_timepoint["arrival_time"]); | if ($next_timepoint["arrival_time"] != "") { |
//echo strtotime($next_timepoint["arrival_time"])." - ".strtotime($cur_timepoint["arrival_time"])."<br>"; | $total_time = strtotime($next_timepoint["arrival_time"]) - strtotime($cur_timepoint["arrival_time"]); |
$time_estimate = ($distance_percent * $total_time) + strtotime($cur_timepoint["arrival_time"]); | // echo strtotime($next_timepoint["arrival_time"])." - ".strtotime($cur_timepoint["arrival_time"])."<br>"; |
$stopTime["arrival_time"] = date("H:i:s", $time_estimate); | $time_estimate = ($distance_percent * $total_time) + strtotime($cur_timepoint["arrival_time"]); |
} else { | $stopTime["arrival_time"] = date("H:i:s", $time_estimate); |
$stopTime["arrival_time"] = $cur_timepoint["arrival_time"]; | } else { |
} | $stopTime["arrival_time"] = $cur_timepoint["arrival_time"]; |
$rv[] = $stopTime; | } |
//var_dump($rv); | $rv[] = $stopTime; |
} | } |
} | } |
return $rv; | // var_dump($rv); |
} | return $rv; |
function getTimeInterpolatedTripAtStop($tripID, $stop_sequence) | } |
{ | |
function getTripPreviousTimePoint($tripID, $stop_sequence) { | |
global $conn; | |
$query = " SELECT trip_id,stop_id, | |
stop_sequence | |
FROM stop_times | |
WHERE trip_id = :tripID and stop_sequence < :stop_sequence | |
and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence DESC LIMIT 1"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":tripID", $tripID); | |
$query->bindParam(":stop_sequence", $stop_sequence); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getTripNextTimePoint($tripID, $stop_sequence) { | |
global $conn; | |
$query = " SELECT trip_id,stop_id, | |
stop_sequence | |
FROM stop_times | |
WHERE trip_id = :tripID and stop_sequence > :stop_sequence | |
and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence LIMIT 1"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":tripID", $tripID); | |
$query->bindParam(":stop_sequence", $stop_sequence); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
return $query->fetch(PDO :: FETCH_ASSOC); | |
} | |
function getTimeInterpolatedTripAtStop($tripID, $stop_sequence) { | |
global $conn; | global $conn; |
// limit interpolation to between nearest actual points. | // limit interpolation to between nearest actual points. |
$prevTimePoint = pg_fetch_assoc(pg_query($conn," SELECT trip_id,stop_id, | $prevTimePoint = getTripPreviousTimePoint($tripID, $stop_sequence); |
stop_sequence | $nextTimePoint = getTripNextTimePoint($tripID, $stop_sequence); |
FROM stop_times | // echo " prev {$lowestDelta['stop_sequence']} next {$nextTimePoint['stop_sequence']} "; |
WHERE trip_id = '$tripID' and stop_sequence < $stop_sequence and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence DESC LIMIT 1")); | $range = ""; |
$nextTimePoint = pg_fetch_assoc(pg_query($conn," SELECT trip_id,stop_id, | if ($prevTimePoint != "") |
stop_sequence | $range .= " AND stop_sequence >= '{$prevTimePoint['stop_sequence']}'"; |
FROM stop_times | if ($nextTimePoint != "") |
WHERE trip_id = '$tripID' and stop_sequence > $stop_sequence and stop_times.arrival_time IS NOT NULL ORDER BY stop_sequence LIMIT 1")); | $range .= " AND stop_sequence <= '{$nextTimePoint['stop_sequence']}'"; |
$range = "AND stop_sequence >= '{$prevTimePoint['stop_sequence']}' AND stop_sequence <= '{$nextTimePoint['stop_sequence']}'"; | foreach (getTimeInterpolatedTrip($tripID, $range) as $tripStop) { |
foreach (getTimeInterpolatedTrip($tripID,$range) as $tripStop) { | if ($tripStop['stop_sequence'] == $stop_sequence) |
if ($tripStop['stop_sequence'] == $stop_sequence) return $tripStop; | return $tripStop; |
} | } |
return Array(); | return Array(); |
} | } |
function getTripStartTime($tripID) | |
{ | function getTripStartTime($tripID) { |
global $conn; | global $conn; |
$query = "Select * from stop_times | $query = "Select * from stop_times |
where trip_id = '$tripID' | where trip_id = :tripID |
AND arrival_time IS NOT NULL | AND arrival_time IS NOT NULL |
AND stop_sequence = '1'"; | AND stop_sequence = '1'"; |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":tripID", $tripID); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
$r = pg_fetch_assoc($result); | return Array(); |
return $r['arrival_time']; | } |
} | $r = $query->fetch(PDO :: FETCH_ASSOC); |
function getActiveTrips($time) | return $r['arrival_time']; |
{ | } |
global $conn; | |
if ($time == "") $time = current_time(); | function getTripEndTime($tripID) { |
$query = "Select distinct stop_times.trip_id, start_times.arrival_time as start_time, end_times.arrival_time as end_time from stop_times, (SELECT trip_id,arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL | global $conn; |
$query = "SELECT trip_id,max(arrival_time) as arrival_time from stop_times | |
WHERE stop_times.arrival_time IS NOT NULL and trip_id = :tripID group by trip_id"; | |
debug($query, "database"); | |
$query = $conn->prepare($query); | |
$query->bindParam(":tripID", $tripID); | |
$query->execute(); | |
if (!$query) { | |
databaseError($conn->errorInfo()); | |
return Array(); | |
} | |
$r = $query->fetch(PDO :: FETCH_ASSOC); | |
return $r['arrival_time']; | |
} | |
function getActiveTrips($time) { | |
global $conn; | |
if ($time == "") | |
$time = current_time(); | |
$query = "Select distinct stop_times.trip_id, start_times.arrival_time as start_time, end_times.arrival_time as end_time from stop_times, (SELECT trip_id,arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL | |
AND stop_sequence = '1') as start_times, (SELECT trip_id,max(arrival_time) as arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times | AND stop_sequence = '1') as start_times, (SELECT trip_id,max(arrival_time) as arrival_time from stop_times WHERE stop_times.arrival_time IS NOT NULL group by trip_id) as end_times |
WHERE start_times.trip_id = end_times.trip_id AND stop_times.trip_id = end_times.trip_id AND $time > start_times.arrival_time AND $time < end_times.arrival_time"; | WHERE start_times.trip_id = end_times.trip_id AND stop_times.trip_id = end_times.trip_id AND :time > start_times.arrival_time AND :time < end_times.arrival_time"; |
debug($query, "database"); | debug($query, "database"); |
$result = pg_query($conn, $query); | $query = $conn->prepare($query); |
if (!$result) { | $query->bindParam(":time", $time); |
databaseError(pg_result_error($result)); | $query->execute(); |
return Array(); | if (!$query) { |
} | databaseError($conn->errorInfo()); |
return pg_fetch_all($result); | return Array(); |
} | } |
return $query->fetchAll(); | |
function viaPoints($tripid, $stop_sequence = "") | } |
{ | |
global $conn; | function viaPoints($tripID, $stop_sequence = "", $timing_points_only = true) { |
$query = "SELECT stops.stop_id, stop_name, arrival_time | global $conn; |
$query = "SELECT stops.stop_id, stop_name, arrival_time | |
FROM stop_times join stops on stops.stop_id = stop_times.stop_id | FROM stop_times join stops on stops.stop_id = stop_times.stop_id |
WHERE stop_times.trip_id = '$tripid' | WHERE stop_times.trip_id = :tripID |
".($stop_sequence != "" ? "AND stop_sequence > '$stop_sequence'" : ""). | " . ($stop_sequence != "" ? " AND stop_sequence > :stop_sequence " : "") . ($timing_points_only ? "AND substr(stop_code,1,2) != 'Wj' " : "") . " ORDER BY stop_sequence"; |
"AND substr(stop_code,1,2) != 'Wj' ORDER BY stop_sequence"; | debug($query, "database"); |
debug($query, "database"); | $query = $conn->prepare($query); |
$result = pg_query($conn, $query); | if ($stop_sequence != "") |
if (!$result) { | $query->bindParam(":stop_sequence", $stop_sequence); |
databaseError(pg_result_error($result)); | $query->bindParam(":tripID", $tripID); |
return Array(); | $query->execute(); |
} | if (!$query) { |
return pg_fetch_all($result); | databaseError($conn->errorInfo()); |
} | return Array(); |
function viaPointNames($tripid, $stop_sequence = "") | } |
{ | return $query->fetchAll(); |
$viaPointNames = Array(); | } |
foreach(viaPoints($tripid, $stop_sequence) as $point) { | |
$viaPointNames[] = $point['stop_name']; | function viaPointNames($tripid, $stop_sequence = "") { |
} | $viaPointNames = Array(); |
return r_implode(", ", $viaPointNames); | foreach (viaPoints($tripid, $stop_sequence) as $point) { |
} | $viaPointNames[] = $point['stop_name']; |
} | |
if (sizeof($viaPointNames) > 0) { | |
return r_implode(", ", $viaPointNames); | |
} else { | |
return ""; | |
} | |
} | |
?> | ?> |
<?php | <?php |
/* | |
* Copyright 2010,2011 Alexander Sadleir | |
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. | |
*/ | |
include ('include/common.inc.php'); | include ('include/common.inc.php'); |
include_header("bus.lambdacomplex.org", "index", false) | include_header("bus.lambdacomplex.org", "index", false) |
?> | ?> |
<div data-role="page"> | <div data-role="page"> |
<div data-role="content"> | <div data-role="content"> |
<div id="jqm-homeheader"> | <div id="jqm-homeheader"> |
<h1>busness time</h1><br><small>Canberra Bus Timetables and Trip Planner</small> | <h1>busness time</h1><br><small>Canberra Bus Timetables and Trip Planner</small> |
</div> | </div> |
<a name="maincontent" id="maincontent"></a> | <a name="maincontent" id="maincontent"></a> |
<a href="tripPlanner.php" data-role="button" data-icon="navigation">Launch Trip Planner...</a> | <a href="tripPlanner.php" data-role="button" data-icon="navigation">Launch Trip Planner...</a> |
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> | <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> |
<li data-role="list-divider">Timetables - Stops</li> | <li data-role="list-divider">Timetables - Stops</li> |
<li><a href="stopList.php">Major (Timing Point) Stops</a></li> | <li><a href="stopList.php">Major (Timing Point) Stops</a></li> |
<li><a href="stopList.php?allstops=yes">All Stops</a></li> | <li><a href="stopList.php?allstops=yes">All Stops</a></li> |
<li><a href="stopList.php?bysuburbs=yes">Stops By Suburb</a></li> | <li><a href="stopList.php?bysuburbs=yes">Stops By Suburb</a></li> |
<li><a class="nearby" href="stopList.php?nearby=yes">Nearby Stops</a></li> | <li><a class="nearby" href="stopList.php?nearby=yes">Nearby Stops</a></li> |
</ul> | </ul> |
<ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> | <ul data-role="listview" data-inset="true" data-theme="c" data-dividertheme="b"> |
<li data-role="list-divider">Timetables - Routes</li> | <li data-role="list-divider">Timetables - Routes</li> |
<li><a href="routeList.php">Routes By Final Destination</a></li> | <li><a href="routeList.php">Routes By Final Destination</a></li> |
<li><a href="routeList.php?bynumber=yes">Routes By Number</a></li> | <li><a href="routeList.php?bynumber=yes">Routes By Number</a></li> |
<li><a href="routeList.php?bysuburbs=yes">Routes By Suburb</a></li> | <li><a href="routeList.php?bysuburbs=yes">Routes By Suburb</a></li> |
<li><a class="nearby" href="routeList.php?nearby=yes">Nearby Routes</a></li> | <li><a class="nearby" href="routeList.php?nearby=yes">Nearby Routes</a></li> |
</ul> | </ul> |
<?php | <?php |
echo timePlaceSettings(); | echo ' <a href="labs/index.php" data-role="button" data-icon="beaker">Busness R&D</a>'; |
echo ' <a href="labs/index.php" data-role="button" data-icon="beaker">Busness R&D</a>'; | echo ' <a href="myway/index.php" data-role="button">MyWay Balance and Timeliness Survey Results</a>'; |
include_footer(true) | include_footer(true) |
?> | ?> |
/*! LAB.js (LABjs :: Loading And Blocking JavaScript) | |
v2.0.1 (c) Kyle Simpson | |
MIT License | |
*/ | |
(function(o){var K=o.$LAB,y="UseLocalXHR",z="AlwaysPreserveOrder",u="AllowDuplicates",A="CacheBust",B="BasePath",C=/^[^?#]*\//.exec(location.href)[0],D=/^\w+\:\/\/\/?[^\/]+/.exec(C)[0],i=document.head||document.getElementsByTagName("head"),L=(o.opera&&Object.prototype.toString.call(o.opera)=="[object Opera]")||("MozAppearance"in document.documentElement.style),q=document.createElement("script"),E=typeof q.preload=="boolean",r=E||(q.readyState&&q.readyState=="uninitialized"),F=!r&&q.async===true,M=!r&&!F&&!L;function G(a){return Object.prototype.toString.call(a)=="[object Function]"}function H(a){return Object.prototype.toString.call(a)=="[object Array]"}function N(a,c){var b=/^\w+\:\/\//;if(/^\/\/\/?/.test(a)){a=location.protocol+a}else if(!b.test(a)&&a.charAt(0)!="/"){a=(c||"")+a}return b.test(a)?a:((a.charAt(0)=="/"?D:C)+a)}function s(a,c){for(var b in a){if(a.hasOwnProperty(b)){c[b]=a[b]}}return c}function O(a){var c=false;for(var b=0;b<a.scripts.length;b++){if(a.scripts[b].ready&&a.scripts[b].exec_trigger){c=true;a.scripts[b].exec_trigger();a.scripts[b].exec_trigger=null}}return c}function t(a,c,b,d){a.onload=a.onreadystatechange=function(){if((a.readyState&&a.readyState!="complete"&&a.readyState!="loaded")||c[b])return;a.onload=a.onreadystatechange=null;d()}}function I(a){a.ready=a.finished=true;for(var c=0;c<a.finished_listeners.length;c++){setTimeout(a.finished_listeners[c],0)}a.ready_listeners=[];a.finished_listeners=[]}function P(d,f,e,g,h){setTimeout(function(){var a,c=f.real_src,b;if("item"in i){if(!i[0]){setTimeout(arguments.callee,25);return}i=i[0]}a=document.createElement("script");if(f.type)a.type=f.type;if(f.charset)a.charset=f.charset;if(h){if(r){e.elem=a;if(E){a.preload=true;a.onpreload=g}else{a.onreadystatechange=function(){if(a.readyState=="loaded")g();a.onreadystatechange=null}}a.src=c}else if(h&&c.indexOf(D)==0&&d[y]){b=new XMLHttpRequest();b.onreadystatechange=function(){if(b.readyState==4){b.onreadystatechange=function(){};e.text=b.responseText+"\n//@ sourceURL="+c;g()}};b.open("GET",c);b.send()}else{a.type="text/cache-script";t(a,e,"ready",function(){i.removeChild(a);g()});a.src=c;i.insertBefore(a,i.firstChild)}}else if(F){a.async=false;t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}else{t(a,e,"finished",g);a.src=c;i.insertBefore(a,i.firstChild)}},0)}function J(){var l={},Q=r||M,n=[],p={},m;l[y]=true;l[z]=false;l[u]=false;l[A]=false;l[B]="";function R(a,c,b){var d;function f(){if(d!=null){I(b);d=null}}if(p[c.src].finished)return;if(!a[u])p[c.src].finished=true;d=b.elem||document.createElement("script");if(c.type)d.type=c.type;if(c.charset)d.charset=c.charset;t(d,b,"finished",f);if(b.elem){b.elem=null}else if(b.text){d.onload=d.onreadystatechange=null;d.text=b.text}else{d.src=c.real_src}i.insertBefore(d,i.firstChild);if(b.text){f()}}function S(c,b,d,f){var e,g,h=function(){b.ready_cb(b,function(){R(c,b,e)})},j=function(){b.finished_cb(b,d)};b.src=N(b.src,c[B]);b.real_src=b.src+(c[A]?((/\?.*$/.test(b.src)?"&_":"?_")+~~(Math.random()*1E9)+"="):"");if(!p[b.src])p[b.src]={items:[],finished:false};g=p[b.src].items;if(c[u]||g.length==0){e=g[g.length]={ready:false,finished:false,ready_listeners:[h],finished_listeners:[j]};P(c,b,e,((f)?function(){e.ready=true;for(var a=0;a<e.ready_listeners.length;a++){setTimeout(e.ready_listeners[a],0)}e.ready_listeners=[]}:function(){I(e)}),f)}else{e=g[0];if(e.finished){setTimeout(j,0)}else{e.finished_listeners.push(j)}}}function v(){var e,g=s(l,{}),h=[],j=0,w=false,k;function T(a,c){a.ready=true;a.exec_trigger=c;x()}function U(a,c){a.ready=a.finished=true;a.exec_trigger=null;for(var b=0;b<c.scripts.length;b++){if(!c.scripts[b].finished)return}c.finished=true;x()}function x(){while(j<h.length){if(G(h[j])){try{h[j]()}catch(err){}}else if(!h[j].finished){if(O(h[j]))continue;break}j++}if(j==h.length){w=false;k=false}}function V(){if(!k||!k.scripts){h.push(k={scripts:[],finished:true})}}e={script:function(){for(var f=0;f<arguments.length;f++){(function(a,c){var b;if(!H(a)){c=[a]}for(var d=0;d<c.length;d++){V();a=c[d];if(G(a))a=a();if(!a)continue;if(H(a)){b=[].slice.call(a);b.push(d,1);c.splice.call(c,b);d--;continue}if(typeof a=="string")a={src:a};a=s(a,{ready:false,ready_cb:T,finished:false,finished_cb:U});k.finished=false;k.scripts.push(a);S(g,a,k,(Q&&w));w=true;if(g[z])e.wait()}})(arguments[f],arguments[f])}return e},wait:function(){if(arguments.length>0){for(var a=0;a<arguments.length;a++){h.push(arguments[a])}k=h[h.length-1]}else k=false;x();return e}};return{script:e.script,wait:e.wait,setOptions:function(a){s(a,g);return e}}}m={setGlobalDefaults:function(a){s(a,l);return m},setOptions:function(){return v().setOptions.apply(null,arguments)},script:function(){return v().script.apply(null,arguments)},wait:function(){return v().wait.apply(null,arguments)},queueScript:function(){n[n.length]={type:"script",args:[].slice.call(arguments)};return m},queueWait:function(){n[n.length]={type:"wait",args:[].slice.call(arguments)};return m},runQueue:function(){var a=m,c=n.length,b=c,d;for(;--b>=0;){d=n.shift();a=a[d.type].apply(null,d.args)}return a},noConflict:function(){o.$LAB=K;return m},sandbox:function(){return J()}};return m}o.$LAB=J();(function(a,c,b){if(document.readyState==null&&document[a]){document.readyState="loading";document[a](c,b=function(){document.removeEventListener(c,b,false);document.readyState="complete"},false)}})("addEventListener","DOMContentLoaded")})(this); |
// Copyright 2006 Google Inc. | |
// | |
// 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. | |
// Known Issues: | |
// | |
// * Patterns only support repeat. | |
// * Radial gradient are not implemented. The VML version of these look very | |
// different from the canvas one. | |
// * Clipping paths are not implemented. | |
// * Coordsize. The width and height attribute have higher priority than the | |
// width and height style values which isn't correct. | |
// * Painting mode isn't implemented. | |
// * Canvas width/height should is using content-box by default. IE in | |
// Quirks mode will draw the canvas using border-box. Either change your | |
// doctype to HTML5 | |
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) | |
// or use Box Sizing Behavior from WebFX | |
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) | |
// * Non uniform scaling does not correctly scale strokes. | |
// * Filling very large shapes (above 5000 points) is buggy. | |
// * Optimize. There is always room for speed improvements. | |
// Only add this code if we do not already have a canvas implementation | |
if (!document.createElement('canvas').getContext) { | |
(function() { | |
// alias some functions to make (compiled) code shorter | |
var m = Math; | |
var mr = m.round; | |
var ms = m.sin; | |
var mc = m.cos; | |
var abs = m.abs; | |
var sqrt = m.sqrt; | |
// this is used for sub pixel precision | |
var Z = 10; | |
var Z2 = Z / 2; | |
/** | |
* This funtion is assigned to the <canvas> elements as element.getContext(). | |
* @this {HTMLElement} | |
* @return {CanvasRenderingContext2D_} | |
*/ | |
function getContext() { | |
return this.context_ || | |
(this.context_ = new CanvasRenderingContext2D_(this)); | |
} | |
var slice = Array.prototype.slice; | |
/** | |
* Binds a function to an object. The returned function will always use the | |
* passed in {@code obj} as {@code this}. | |
* | |
* Example: | |
* | |
* g = bind(f, obj, a, b) | |
* g(c, d) // will do f.call(obj, a, b, c, d) | |
* | |
* @param {Function} f The function to bind the object to | |
* @param {Object} obj The object that should act as this when the function | |
* is called | |
* @param {*} var_args Rest arguments that will be used as the initial | |
* arguments when the function is called | |
* @return {Function} A new function that has bound this | |
*/ | |
function bind(f, obj, var_args) { | |
var a = slice.call(arguments, 2); | |
return function() { | |
return f.apply(obj, a.concat(slice.call(arguments))); | |
}; | |
} | |
function encodeHtmlAttribute(s) { | |
return String(s).replace(/&/g, '&').replace(/"/g, '"'); | |
} | |
function addNamespacesAndStylesheet(doc) { | |
// create xmlns | |
if (!doc.namespaces['g_vml_']) { | |
doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', | |
'#default#VML'); | |
} | |
if (!doc.namespaces['g_o_']) { | |
doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', | |
'#default#VML'); | |
} | |
// Setup default CSS. Only add one style sheet per document | |
if (!doc.styleSheets['ex_canvas_']) { | |
var ss = doc.createStyleSheet(); | |
ss.owningElement.id = 'ex_canvas_'; | |
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + | |
// default size is 300x150 in Gecko and Opera | |
'text-align:left;width:300px;height:150px}'; | |
} | |
} | |
// Add namespaces and stylesheet at startup. | |
addNamespacesAndStylesheet(document); | |
var G_vmlCanvasManager_ = { | |
init: function(opt_doc) { | |
if (/MSIE/.test(navigator.userAgent) && !window.opera) { | |
var doc = opt_doc || document; | |
// Create a dummy element so that IE will allow canvas elements to be | |
// recognized. | |
doc.createElement('canvas'); | |
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); | |
} | |
}, | |
init_: function(doc) { | |
// find all canvas elements | |
var els = doc.getElementsByTagName('canvas'); | |
for (var i = 0; i < els.length; i++) { | |
this.initElement(els[i]); | |
} | |
}, | |
/** | |
* Public initializes a canvas element so that it can be used as canvas | |
* element from now on. This is called automatically before the page is | |
* loaded but if you are creating elements using createElement you need to | |
* make sure this is called on the element. | |
* @param {HTMLElement} el The canvas element to initialize. | |
* @return {HTMLElement} the element that was created. | |
*/ | |
initElement: function(el) { | |
if (!el.getContext) { | |
el.getContext = getContext; | |
// Add namespaces and stylesheet to document of the element. | |
addNamespacesAndStylesheet(el.ownerDocument); | |
// Remove fallback content. There is no way to hide text nodes so we | |
// just remove all childNodes. We could hide all elements and remove | |
// text nodes but who really cares about the fallback content. | |
el.innerHTML = ''; | |
// do not use inline function because that will leak memory | |
el.attachEvent('onpropertychange', onPropertyChange); | |
el.attachEvent('onresize', onResize); | |
var attrs = el.attributes; | |
if (attrs.width && attrs.width.specified) { | |
// TODO: use runtimeStyle and coordsize | |
// el.getContext().setWidth_(attrs.width.nodeValue); | |
el.style.width = attrs.width.nodeValue + 'px'; | |
} else { | |
el.width = el.clientWidth; | |
} | |
if (attrs.height && attrs.height.specified) { | |
// TODO: use runtimeStyle and coordsize | |
// el.getContext().setHeight_(attrs.height.nodeValue); | |
el.style.height = attrs.height.nodeValue + 'px'; | |
} else { | |
el.height = el.clientHeight; | |
} | |
//el.getContext().setCoordsize_() | |
} | |
return el; | |
} | |
}; | |
function onPropertyChange(e) { | |
var el = e.srcElement; | |
switch (e.propertyName) { | |
case 'width': | |
el.getContext().clearRect(); | |
el.style.width = el.attributes.width.nodeValue + 'px'; | |
// In IE8 this does not trigger onresize. | |
el.firstChild.style.width = el.clientWidth + 'px'; | |
break; | |
case 'height': | |
el.getContext().clearRect(); | |
el.style.height = el.attributes.height.nodeValue + 'px'; | |
el.firstChild.style.height = el.clientHeight + 'px'; | |
break; | |
} | |
} | |
function onResize(e) { | |
var el = e.srcElement; | |
if (el.firstChild) { | |
el.firstChild.style.width = el.clientWidth + 'px'; | |
el.firstChild.style.height = el.clientHeight + 'px'; | |
} | |
} | |
G_vmlCanvasManager_.init(); | |
// precompute "00" to "FF" | |
var decToHex = []; | |
for (var i = 0; i < 16; i++) { | |
for (var j = 0; j < 16; j++) { | |
decToHex[i * 16 + j] = i.toString(16) + j.toString(16); | |
} | |
} | |
function createMatrixIdentity() { | |
return [ | |
[1, 0, 0], | |
[0, 1, 0], | |
[0, 0, 1] | |
]; | |
} | |
function matrixMultiply(m1, m2) { | |
var result = createMatrixIdentity(); | |
for (var x = 0; x < 3; x++) { | |
for (var y = 0; y < 3; y++) { | |
var sum = 0; | |
for (var z = 0; z < 3; z++) { | |
sum += m1[x][z] * m2[z][y]; | |
} | |
result[x][y] = sum; | |
} | |
} | |
return result; | |
} | |
function copyState(o1, o2) { | |
o2.fillStyle = o1.fillStyle; | |
o2.lineCap = o1.lineCap; | |
o2.lineJoin = o1.lineJoin; | |
o2.lineWidth = o1.lineWidth; | |
o2.miterLimit = o1.miterLimit; | |
o2.shadowBlur = o1.shadowBlur; | |
o2.shadowColor = o1.shadowColor; | |
o2.shadowOffsetX = o1.shadowOffsetX; | |
o2.shadowOffsetY = o1.shadowOffsetY; | |
o2.strokeStyle = o1.strokeStyle; | |
o2.globalAlpha = o1.globalAlpha; | |
o2.font = o1.font; | |
o2.textAlign = o1.textAlign; | |
o2.textBaseline = o1.textBaseline; | |
o2.arcScaleX_ = o1.arcScaleX_; | |
o2.arcScaleY_ = o1.arcScaleY_; | |
o2.lineScale_ = o1.lineScale_; | |
} | |
var colorData = { | |
aliceblue: '#F0F8FF', | |
antiquewhite: '#FAEBD7', | |
aquamarine: '#7FFFD4', | |
azure: '#F0FFFF', | |
beige: '#F5F5DC', | |
bisque: '#FFE4C4', | |
black: '#000000', | |
blanchedalmond: '#FFEBCD', | |
blueviolet: '#8A2BE2', | |
brown: '#A52A2A', | |
burlywood: '#DEB887', | |
cadetblue: '#5F9EA0', | |
chartreuse: '#7FFF00', | |
chocolate: '#D2691E', | |
coral: '#FF7F50', | |
cornflowerblue: '#6495ED', | |
cornsilk: '#FFF8DC', | |
crimson: '#DC143C', | |
cyan: '#00FFFF', | |
darkblue: '#00008B', | |
darkcyan: '#008B8B', | |
darkgoldenrod: '#B8860B', | |
darkgray: '#A9A9A9', | |
darkgreen: '#006400', | |
darkgrey: '#A9A9A9', | |
darkkhaki: '#BDB76B', | |
darkmagenta: '#8B008B', | |
darkolivegreen: '#556B2F', | |
darkorange: '#FF8C00', | |
darkorchid: '#9932CC', | |
darkred: '#8B0000', | |
darksalmon: '#E9967A', | |
darkseagreen: '#8FBC8F', | |
darkslateblue: '#483D8B', | |
darkslategray: '#2F4F4F', | |
darkslategrey: '#2F4F4F', | |
darkturquoise: '#00CED1', | |
darkviolet: '#9400D3', | |
deeppink: '#FF1493', | |
deepskyblue: '#00BFFF', | |
dimgray: '#696969', | |
dimgrey: '#696969', | |
dodgerblue: '#1E90FF', | |
firebrick: '#B22222', | |
floralwhite: '#FFFAF0', | |
forestgreen: '#228B22', | |
gainsboro: '#DCDCDC', | |
ghostwhite: '#F8F8FF', | |
gold: '#FFD700', | |
goldenrod: '#DAA520', | |
grey: '#808080', | |
greenyellow: '#ADFF2F', | |
honeydew: '#F0FFF0', | |
hotpink: '#FF69B4', | |
indianred: '#CD5C5C', | |
indigo: '#4B0082', | |
ivory: '#FFFFF0', | |
khaki: '#F0E68C', | |
lavender: '#E6E6FA', | |
lavenderblush: '#FFF0F5', | |
lawngreen: '#7CFC00', | |
lemonchiffon: '#FFFACD', | |
lightblue: '#ADD8E6', | |
lightcoral: '#F08080', | |
lightcyan: '#E0FFFF', | |
lightgoldenrodyellow: '#FAFAD2', | |
lightgreen: '#90EE90', | |
lightgrey: '#D3D3D3', | |
lightpink: '#FFB6C1', | |
lightsalmon: '#FFA07A', | |
lightseagreen: '#20B2AA', | |
lightskyblue: '#87CEFA', | |
lightslategray: '#778899', | |
lightslategrey: '#778899', | |
lightsteelblue: '#B0C4DE', | |
lightyellow: '#FFFFE0', | |
limegreen: '#32CD32', | |
linen: '#FAF0E6', | |
magenta: '#FF00FF', | |
mediumaquamarine: '#66CDAA', | |
mediumblue: '#0000CD', | |
mediumorchid: '#BA55D3', | |
mediumpurple: '#9370DB', | |
mediumseagreen: '#3CB371', | |
mediumslateblue: '#7B68EE', | |
mediumspringgreen: '#00FA9A', | |
mediumturquoise: '#48D1CC', | |
mediumvioletred: '#C71585', | |
midnightblue: '#191970', | |
mintcream: '#F5FFFA', | |
mistyrose: '#FFE4E1', | |
moccasin: '#FFE4B5', | |
navajowhite: '#FFDEAD', | |
oldlace: '#FDF5E6', | |
olivedrab: '#6B8E23', | |
orange: '#FFA500', | |
orangered: '#FF4500', | |
orchid: '#DA70D6', | |
palegoldenrod: '#EEE8AA', | |
palegreen: '#98FB98', | |
paleturquoise: '#AFEEEE', | |
palevioletred: '#DB7093', | |
papayawhip: '#FFEFD5', | |
peachpuff: '#FFDAB9', | |
peru: '#CD853F', | |
pink: '#FFC0CB', | |
plum: '#DDA0DD', | |
powderblue: '#B0E0E6', | |
rosybrown: '#BC8F8F', | |
royalblue: '#4169E1', | |
saddlebrown: '#8B4513', | |
salmon: '#FA8072', | |
sandybrown: '#F4A460', | |
seagreen: '#2E8B57', | |
seashell: '#FFF5EE', | |
sienna: '#A0522D', | |
skyblue: '#87CEEB', | |
slateblue: '#6A5ACD', | |
slategray: '#708090', | |
slategrey: '#708090', | |
snow: '#FFFAFA', | |
springgreen: '#00FF7F', | |
steelblue: '#4682B4', | |
tan: '#D2B48C', | |
thistle: '#D8BFD8', | |
tomato: '#FF6347', | |
turquoise: '#40E0D0', | |
violet: '#EE82EE', | |
wheat: '#F5DEB3', | |
whitesmoke: '#F5F5F5', | |
yellowgreen: '#9ACD32' | |
}; | |
function getRgbHslContent(styleString) { | |
var start = styleString.indexOf('(', 3); | |
var end = styleString.indexOf(')', start + 1); | |
var parts = styleString.substring(start + 1, end).split(','); | |
// add alpha if needed | |
if (parts.length == 4 && styleString.substr(3, 1) == 'a') { | |
alpha = Number(parts[3]); | |
} else { | |
parts[3] = 1; | |
} | |
return parts; | |
} | |
function percent(s) { | |
return parseFloat(s) / 100; | |
} | |
function clamp(v, min, max) { | |
return Math.min(max, Math.max(min, v)); | |
} | |
function hslToRgb(parts){ | |
var r, g, b; | |
h = parseFloat(parts[0]) / 360 % 360; | |
if (h < 0) | |
h++; | |
s = clamp(percent(parts[1]), 0, 1); | |
l = clamp(percent(parts[2]), 0, 1); | |
if (s == 0) { | |
r = g = b = l; // achromatic | |
} else { | |
var q = l < 0.5 ? l * (1 + s) : l + s - l * s; | |
var p = 2 * l - q; | |
r = hueToRgb(p, q, h + 1 / 3); | |
g = hueToRgb(p, q, h); | |
b = hueToRgb(p, q, h - 1 / 3); | |
} | |
return '#' + decToHex[Math.floor(r * 255)] + | |
decToHex[Math.floor(g * 255)] + | |
decToHex[Math.floor(b * 255)]; | |
} | |
function hueToRgb(m1, m2, h) { | |
if (h < 0) | |
h++; | |
if (h > 1) | |
h--; | |
if (6 * h < 1) | |
return m1 + (m2 - m1) * 6 * h; | |
else if (2 * h < 1) | |
return m2; | |
else if (3 * h < 2) | |
return m1 + (m2 - m1) * (2 / 3 - h) * 6; | |
else | |
return m1; | |
} | |
function processStyle(styleString) { | |
var str, alpha = 1; | |
styleString = String(styleString); | |
if (styleString.charAt(0) == '#') { | |
str = styleString; | |
} else if (/^rgb/.test(styleString)) { | |
var parts = getRgbHslContent(styleString); | |
var str = '#', n; | |
for (var i = 0; i < 3; i++) { | |
if (parts[i].indexOf('%') != -1) { | |
n = Math.floor(percent(parts[i]) * 255); | |
} else { | |
n = Number(parts[i]); | |
} | |
str += decToHex[clamp(n, 0, 255)]; | |
} | |
alpha = parts[3]; | |
} else if (/^hsl/.test(styleString)) { | |
var parts = getRgbHslContent(styleString); | |
str = hslToRgb(parts); | |
alpha = parts[3]; | |
} else { | |
str = colorData[styleString] || styleString; | |
} | |
return {color: str, alpha: alpha}; | |
} | |
var DEFAULT_STYLE = { | |
style: 'normal', | |
variant: 'normal', | |
weight: 'normal', | |
size: 10, | |
family: 'sans-serif' | |
}; | |
// Internal text style cache | |
var fontStyleCache = {}; | |
function processFontStyle(styleString) { | |
if (fontStyleCache[styleString]) { | |
return fontStyleCache[styleString]; | |
} | |
var el = document.createElement('div'); | |
var style = el.style; | |
try { | |
style.font = styleString; | |
} catch (ex) { | |
// Ignore failures to set to invalid font. | |
} | |
return fontStyleCache[styleString] = { | |
style: style.fontStyle || DEFAULT_STYLE.style, | |
variant: style.fontVariant || DEFAULT_STYLE.variant, | |
weight: style.fontWeight || DEFAULT_STYLE.weight, | |
size: style.fontSize || DEFAULT_STYLE.size, | |
family: style.fontFamily || DEFAULT_STYLE.family | |
}; | |
} | |
function getComputedStyle(style, element) { | |
var computedStyle = {}; | |
for (var p in style) { | |
computedStyle[p] = style[p]; | |
} | |
// Compute the size | |
var canvasFontSize = parseFloat(element.currentStyle.fontSize), | |
fontSize = parseFloat(style.size); | |
if (typeof style.size == 'number') { | |
computedStyle.size = style.size; | |
} else if (style.size.indexOf('px') != -1) { | |
computedStyle.size = fontSize; | |
} else if (style.size.indexOf('em') != -1) { | |
computedStyle.size = canvasFontSize * fontSize; | |
} else if(style.size.indexOf('%') != -1) { | |
computedStyle.size = (canvasFontSize / 100) * fontSize; | |
} else if (style.size.indexOf('pt') != -1) { | |
computedStyle.size = fontSize / .75; | |
} else { | |
computedStyle.size = canvasFontSize; | |
} | |
// Different scaling between normal text and VML text. This was found using | |
// trial and error to get the same size as non VML text. | |
computedStyle.size *= 0.981; | |
return computedStyle; | |
} | |
function buildStyle(style) { | |
return style.style + ' ' + style.variant + ' ' + style.weight + ' ' + | |
style.size + 'px ' + style.family; | |
} | |
function processLineCap(lineCap) { | |
switch (lineCap) { | |
case 'butt': | |
return 'flat'; | |
case 'round': | |
return 'round'; | |
case 'square': | |
default: | |
return 'square'; | |
} | |
} | |
/** | |
* This class implements CanvasRenderingContext2D interface as described by | |
* the WHATWG. | |
* @param {HTMLElement} surfaceElement The element that the 2D context should | |
* be associated with | |
*/ | |
function CanvasRenderingContext2D_(surfaceElement) { | |
this.m_ = createMatrixIdentity(); | |
this.mStack_ = []; | |
this.aStack_ = []; | |
this.currentPath_ = []; | |
// Canvas context properties | |
this.strokeStyle = '#000'; | |
this.fillStyle = '#000'; | |
this.lineWidth = 1; | |
this.lineJoin = 'miter'; | |
this.lineCap = 'butt'; | |
this.miterLimit = Z * 1; | |
this.globalAlpha = 1; | |
this.font = '10px sans-serif'; | |
this.textAlign = 'left'; | |
this.textBaseline = 'alphabetic'; | |
this.canvas = surfaceElement; | |
var el = surfaceElement.ownerDocument.createElement('div'); | |
el.style.width = surfaceElement.clientWidth + 'px'; | |
el.style.height = surfaceElement.clientHeight + 'px'; | |
el.style.overflow = 'hidden'; | |
el.style.position = 'absolute'; | |
surfaceElement.appendChild(el); | |
this.element_ = el; | |
this.arcScaleX_ = 1; | |
this.arcScaleY_ = 1; | |
this.lineScale_ = 1; | |
} | |
var contextPrototype = CanvasRenderingContext2D_.prototype; | |
contextPrototype.clearRect = function() { | |
if (this.textMeasureEl_) { | |
this.textMeasureEl_.removeNode(true); | |
this.textMeasureEl_ = null; | |
} | |
this.element_.innerHTML = ''; | |
}; | |
contextPrototype.beginPath = function() { | |
// TODO: Branch current matrix so that save/restore has no effect | |
// as per safari docs. | |
this.currentPath_ = []; | |
}; | |
contextPrototype.moveTo = function(aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); | |
this.currentX_ = p.x; | |
this.currentY_ = p.y; | |
}; | |
contextPrototype.lineTo = function(aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); | |
this.currentX_ = p.x; | |
this.currentY_ = p.y; | |
}; | |
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, | |
aCP2x, aCP2y, | |
aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
var cp1 = this.getCoords_(aCP1x, aCP1y); | |
var cp2 = this.getCoords_(aCP2x, aCP2y); | |
bezierCurveTo(this, cp1, cp2, p); | |
}; | |
// Helper function that takes the already fixed cordinates. | |
function bezierCurveTo(self, cp1, cp2, p) { | |
self.currentPath_.push({ | |
type: 'bezierCurveTo', | |
cp1x: cp1.x, | |
cp1y: cp1.y, | |
cp2x: cp2.x, | |
cp2y: cp2.y, | |
x: p.x, | |
y: p.y | |
}); | |
self.currentX_ = p.x; | |
self.currentY_ = p.y; | |
} | |
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { | |
// the following is lifted almost directly from | |
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes | |
var cp = this.getCoords_(aCPx, aCPy); | |
var p = this.getCoords_(aX, aY); | |
var cp1 = { | |
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), | |
y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) | |
}; | |
var cp2 = { | |
x: cp1.x + (p.x - this.currentX_) / 3.0, | |
y: cp1.y + (p.y - this.currentY_) / 3.0 | |
}; | |
bezierCurveTo(this, cp1, cp2, p); | |
}; | |
contextPrototype.arc = function(aX, aY, aRadius, | |
aStartAngle, aEndAngle, aClockwise) { | |
aRadius *= Z; | |
var arcType = aClockwise ? 'at' : 'wa'; | |
var xStart = aX + mc(aStartAngle) * aRadius - Z2; | |
var yStart = aY + ms(aStartAngle) * aRadius - Z2; | |
var xEnd = aX + mc(aEndAngle) * aRadius - Z2; | |
var yEnd = aY + ms(aEndAngle) * aRadius - Z2; | |
// IE won't render arches drawn counter clockwise if xStart == xEnd. | |
if (xStart == xEnd && !aClockwise) { | |
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something | |
// that can be represented in binary | |
} | |
var p = this.getCoords_(aX, aY); | |
var pStart = this.getCoords_(xStart, yStart); | |
var pEnd = this.getCoords_(xEnd, yEnd); | |
this.currentPath_.push({type: arcType, | |
x: p.x, | |
y: p.y, | |
radius: aRadius, | |
xStart: pStart.x, | |
yStart: pStart.y, | |
xEnd: pEnd.x, | |
yEnd: pEnd.y}); | |
}; | |
contextPrototype.rect = function(aX, aY, aWidth, aHeight) { | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
}; | |
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { | |
var oldPath = this.currentPath_; | |
this.beginPath(); | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
this.stroke(); | |
this.currentPath_ = oldPath; | |
}; | |
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { | |
var oldPath = this.currentPath_; | |
this.beginPath(); | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
this.fill(); | |
this.currentPath_ = oldPath; | |
}; | |
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { | |
var gradient = new CanvasGradient_('gradient'); | |
gradient.x0_ = aX0; | |
gradient.y0_ = aY0; | |
gradient.x1_ = aX1; | |
gradient.y1_ = aY1; | |
return gradient; | |
}; | |
contextPrototype.createRadialGradient = function(aX0, aY0, aR0, | |
aX1, aY1, aR1) { | |
var gradient = new CanvasGradient_('gradientradial'); | |
gradient.x0_ = aX0; | |
gradient.y0_ = aY0; | |
gradient.r0_ = aR0; | |
gradient.x1_ = aX1; | |
gradient.y1_ = aY1; | |
gradient.r1_ = aR1; | |
return gradient; | |
}; | |
contextPrototype.drawImage = function(image, var_args) { | |
var dx, dy, dw, dh, sx, sy, sw, sh; | |
// to find the original width we overide the width and height | |
var oldRuntimeWidth = image.runtimeStyle.width; | |
var oldRuntimeHeight = image.runtimeStyle.height; | |
image.runtimeStyle.width = 'auto'; | |
image.runtimeStyle.height = 'auto'; | |
// get the original size | |
var w = image.width; | |
var h = image.height; | |
// and remove overides | |
image.runtimeStyle.width = oldRuntimeWidth; | |
image.runtimeStyle.height = oldRuntimeHeight; | |
if (arguments.length == 3) { | |
dx = arguments[1]; | |
dy = arguments[2]; | |
sx = sy = 0; | |
sw = dw = w; | |
sh = dh = h; | |
} else if (arguments.length == 5) { | |
dx = arguments[1]; | |
dy = arguments[2]; | |
dw = arguments[3]; | |
dh = arguments[4]; | |
sx = sy = 0; | |
sw = w; | |
sh = h; | |
} else if (arguments.length == 9) { | |
sx = arguments[1]; | |
sy = arguments[2]; | |
sw = arguments[3]; | |
sh = arguments[4]; | |
dx = arguments[5]; | |
dy = arguments[6]; | |
dw = arguments[7]; | |
dh = arguments[8]; | |
} else { | |
throw Error('Invalid number of arguments'); | |
} | |
var d = this.getCoords_(dx, dy); | |
var w2 = sw / 2; | |
var h2 = sh / 2; | |
var vmlStr = []; | |
var W = 10; | |
var H = 10; | |
// For some reason that I've now forgotten, using divs didn't work | |
vmlStr.push(' <g_vml_:group', | |
' coordsize="', Z * W, ',', Z * H, '"', | |
' coordorigin="0,0"' , | |
' style="width:', W, 'px;height:', H, 'px;position:absolute;'); | |
// If filters are necessary (rotation exists), create them | |
// filters are bog-slow, so only create them if abbsolutely necessary | |
// The following check doesn't account for skews (which don't exist | |
// in the canvas spec (yet) anyway. | |
if (this.m_[0][0] != 1 || this.m_[0][1] || | |
this.m_[1][1] != 1 || this.m_[1][0]) { | |
var filter = []; | |
// Note the 12/21 reversal | |
filter.push('M11=', this.m_[0][0], ',', | |
'M12=', this.m_[1][0], ',', | |
'M21=', this.m_[0][1], ',', | |
'M22=', this.m_[1][1], ',', | |
'Dx=', mr(d.x / Z), ',', | |
'Dy=', mr(d.y / Z), ''); | |
// Bounding box calculation (need to minimize displayed area so that | |
// filters don't waste time on unused pixels. | |
var max = d; | |
var c2 = this.getCoords_(dx + dw, dy); | |
var c3 = this.getCoords_(dx, dy + dh); | |
var c4 = this.getCoords_(dx + dw, dy + dh); | |
max.x = m.max(max.x, c2.x, c3.x, c4.x); | |
max.y = m.max(max.y, c2.y, c3.y, c4.y); | |
vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z), | |
'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(', | |
filter.join(''), ", sizingmethod='clip');"); | |
} else { | |
vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;'); | |
} | |
vmlStr.push(' ">' , | |
'<g_vml_:image src="', image.src, '"', | |
' style="width:', Z * dw, 'px;', | |
' height:', Z * dh, 'px"', | |
' cropleft="', sx / w, '"', | |
' croptop="', sy / h, '"', | |
' cropright="', (w - sx - sw) / w, '"', | |
' cropbottom="', (h - sy - sh) / h, '"', | |
' />', | |
'</g_vml_:group>'); | |
this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join('')); | |
}; | |
contextPrototype.stroke = function(aFill) { | |
var W = 10; | |
var H = 10; | |
// Divide the shape into chunks if it's too long because IE has a limit | |
// somewhere for how long a VML shape can be. This simple division does | |
// not work with fills, only strokes, unfortunately. | |
var chunkSize = 5000; | |
var min = {x: null, y: null}; | |
var max = {x: null, y: null}; | |
for (var j = 0; j < this.currentPath_.length; j += chunkSize) { | |
var lineStr = []; | |
var lineOpen = false; | |
lineStr.push('<g_vml_:shape', | |
' filled="', !!aFill, '"', | |
' style="position:absolute;width:', W, 'px;height:', H, 'px;"', | |
' coordorigin="0,0"', | |
' coordsize="', Z * W, ',', Z * H, '"', | |
' stroked="', !aFill, '"', | |
' path="'); | |
var newSeq = false; | |
for (var i = j; i < Math.min(j + chunkSize, this.currentPath_.length); i++) { | |
if (i % chunkSize == 0 && i > 0) { // move into position for next chunk | |
lineStr.push(' m ', mr(this.currentPath_[i-1].x), ',', mr(this.currentPath_[i-1].y)); | |
} | |
var p = this.currentPath_[i]; | |
var c; | |
switch (p.type) { | |
case 'moveTo': | |
c = p; | |
lineStr.push(' m ', mr(p.x), ',', mr(p.y)); | |
break; | |
case 'lineTo': | |
lineStr.push(' l ', mr(p.x), ',', mr(p.y)); | |
break; | |
case 'close': | |
lineStr.push(' x '); | |
p = null; | |
break; | |
case 'bezierCurveTo': | |
lineStr.push(' c ', | |
mr(p.cp1x), ',', mr(p.cp1y), ',', | |
mr(p.cp2x), ',', mr(p.cp2y), ',', | |
mr(p.x), ',', mr(p.y)); | |
break; | |
case 'at': | |
case 'wa': | |
lineStr.push(' ', p.type, ' ', | |
mr(p.x - this.arcScaleX_ * p.radius), ',', | |
mr(p.y - this.arcScaleY_ * p.radius), ' ', | |
mr(p.x + this.arcScaleX_ * p.radius), ',', | |
mr(p.y + this.arcScaleY_ * p.radius), ' ', | |
mr(p.xStart), ',', mr(p.yStart), ' ', | |
mr(p.xEnd), ',', mr(p.yEnd)); | |
break; | |
} | |
// TODO: Following is broken for curves due to | |
// move to proper paths. | |
// Figure out dimensions so we can do gradient fills | |
// properly | |
if (p) { | |
if (min.x == null || p.x < min.x) { | |
min.x = p.x; | |
} | |
if (max.x == null || p.x > max.x) { | |
max.x = p.x; | |
} | |
if (min.y == null || p.y < min.y) { | |
min.y = p.y; | |
} | |
if (max.y == null || p.y > max.y) { | |
max.y = p.y; | |
} | |
} | |
} | |
lineStr.push(' ">'); | |
if (!aFill) { | |
appendStroke(this, lineStr); | |
} else { | |
appendFill(this, lineStr, min, max); | |
} | |
lineStr.push('</g_vml_:shape>'); | |
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); | |
} | |
}; | |
function appendStroke(ctx, lineStr) { | |
var a = processStyle(ctx.strokeStyle); | |
var color = a.color; | |
var opacity = a.alpha * ctx.globalAlpha; | |
var lineWidth = ctx.lineScale_ * ctx.lineWidth; | |
// VML cannot correctly render a line if the width is less than 1px. | |
// In that case, we dilute the color to make the line look thinner. | |
if (lineWidth < 1) { | |
opacity *= lineWidth; | |
} | |
lineStr.push( | |
'<g_vml_:stroke', | |
' opacity="', opacity, '"', | |
' joinstyle="', ctx.lineJoin, '"', | |
' miterlimit="', ctx.miterLimit, '"', | |
' endcap="', processLineCap(ctx.lineCap), '"', | |
' weight="', lineWidth, 'px"', | |
' color="', color, '" />' | |
); | |
} | |
function appendFill(ctx, lineStr, min, max) { | |
var fillStyle = ctx.fillStyle; | |
var arcScaleX = ctx.arcScaleX_; | |
var arcScaleY = ctx.arcScaleY_; | |
var width = max.x - min.x; | |
var height = max.y - min.y; | |
if (fillStyle instanceof CanvasGradient_) { | |
// TODO: Gradients transformed with the transformation matrix. | |
var angle = 0; | |
var focus = {x: 0, y: 0}; | |
// additional offset | |
var shift = 0; | |
// scale factor for offset | |
var expansion = 1; | |
if (fillStyle.type_ == 'gradient') { | |
var x0 = fillStyle.x0_ / arcScaleX; | |
var y0 = fillStyle.y0_ / arcScaleY; | |
var x1 = fillStyle.x1_ / arcScaleX; | |
var y1 = fillStyle.y1_ / arcScaleY; | |
var p0 = ctx.getCoords_(x0, y0); | |
var p1 = ctx.getCoords_(x1, y1); | |
var dx = p1.x - p0.x; | |
var dy = p1.y - p0.y; | |
angle = Math.atan2(dx, dy) * 180 / Math.PI; | |
// The angle should be a non-negative number. | |
if (angle < 0) { | |
angle += 360; | |
} | |
// Very small angles produce an unexpected result because they are | |
// converted to a scientific notation string. | |
if (angle < 1e-6) { | |
angle = 0; | |
} | |
} else { | |
var p0 = ctx.getCoords_(fillStyle.x0_, fillStyle.y0_); | |
focus = { | |
x: (p0.x - min.x) / width, | |
y: (p0.y - min.y) / height | |
}; | |
width /= arcScaleX * Z; | |
height /= arcScaleY * Z; | |
var dimension = m.max(width, height); | |
shift = 2 * fillStyle.r0_ / dimension; | |
expansion = 2 * fillStyle.r1_ / dimension - shift; | |
} | |
// We need to sort the color stops in ascending order by offset, | |
// otherwise IE won't interpret it correctly. | |
var stops = fillStyle.colors_; | |
stops.sort(function(cs1, cs2) { | |
return cs1.offset - cs2.offset; | |
}); | |
var length = stops.length; | |
var color1 = stops[0].color; | |
var color2 = stops[length - 1].color; | |
var opacity1 = stops[0].alpha * ctx.globalAlpha; | |
var opacity2 = stops[length - 1].alpha * ctx.globalAlpha; | |
var colors = []; | |
for (var i = 0; i < length; i++) { | |
var stop = stops[i]; | |
colors.push(stop.offset * expansion + shift + ' ' + stop.color); | |
} | |
// When colors attribute is used, the meanings of opacity and o:opacity2 | |
// are reversed. | |
lineStr.push('<g_vml_:fill type="', fillStyle.type_, '"', | |
' method="none" focus="100%"', | |
' color="', color1, '"', | |
' color2="', color2, '"', | |
' colors="', colors.join(','), '"', | |
' opacity="', opacity2, '"', | |
' g_o_:opacity2="', opacity1, '"', | |
' angle="', angle, '"', | |
' focusposition="', focus.x, ',', focus.y, '" />'); | |
} else if (fillStyle instanceof CanvasPattern_) { | |
if (width && height) { | |
var deltaLeft = -min.x; | |
var deltaTop = -min.y; | |
lineStr.push('<g_vml_:fill', | |
' position="', | |
deltaLeft / width * arcScaleX * arcScaleX, ',', | |
deltaTop / height * arcScaleY * arcScaleY, '"', | |
' type="tile"', | |
// TODO: Figure out the correct size to fit the scale. | |
//' size="', w, 'px ', h, 'px"', | |
' src="', fillStyle.src_, '" />'); | |
} | |
} else { | |
var a = processStyle(ctx.fillStyle); | |
var color = a.color; | |
var opacity = a.alpha * ctx.globalAlpha; | |
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, | |
'" />'); | |
} | |
} | |
contextPrototype.fill = function() { | |
this.stroke(true); | |
}; | |
contextPrototype.closePath = function() { | |
this.currentPath_.push({type: 'close'}); | |
}; | |
/** | |
* @private | |
*/ | |
contextPrototype.getCoords_ = function(aX, aY) { | |
var m = this.m_; | |
return { | |
x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2, | |
y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2 | |
}; | |
}; | |
contextPrototype.save = function() { | |
var o = {}; | |
copyState(this, o); | |
this.aStack_.push(o); | |
this.mStack_.push(this.m_); | |
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_); | |
}; | |
contextPrototype.restore = function() { | |
if (this.aStack_.length) { | |
copyState(this.aStack_.pop(), this); | |
this.m_ = this.mStack_.pop(); | |
} | |
}; | |
function matrixIsFinite(m) { | |
return isFinite(m[0][0]) && isFinite(m[0][1]) && | |
isFinite(m[1][0]) && isFinite(m[1][1]) && | |
isFinite(m[2][0]) && isFinite(m[2][1]); | |
} | |
function setM(ctx, m, updateLineScale) { | |
if (!matrixIsFinite(m)) { | |
return; | |
} | |
ctx.m_ = m; | |
if (updateLineScale) { | |
// Get the line scale. | |
// Determinant of this.m_ means how much the area is enlarged by the | |
// transformation. So its square root can be used as a scale factor | |
// for width. | |
var det = m[0][0] * m[1][1] - m[0][1] * m[1][0]; | |
ctx.lineScale_ = sqrt(abs(det)); | |
} | |
} | |
contextPrototype.translate = function(aX, aY) { | |
var m1 = [ | |
[1, 0, 0], | |
[0, 1, 0], | |
[aX, aY, 1] | |
]; | |
setM(this, matrixMultiply(m1, this.m_), false); | |
}; | |
contextPrototype.rotate = function(aRot) { | |
var c = mc(aRot); | |
var s = ms(aRot); | |
var m1 = [ | |
[c, s, 0], | |
[-s, c, 0], | |
[0, 0, 1] | |
]; | |
setM(this, matrixMultiply(m1, this.m_), false); | |
}; | |
contextPrototype.scale = function(aX, aY) { | |
this.arcScaleX_ *= aX; | |
this.arcScaleY_ *= aY; | |
var m1 = [ | |
[aX, 0, 0], | |
[0, aY, 0], | |
[0, 0, 1] | |
]; | |
setM(this, matrixMultiply(m1, this.m_), true); | |
}; | |
contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) { | |
var m1 = [ | |
[m11, m12, 0], | |
[m21, m22, 0], | |
[dx, dy, 1] | |
]; | |
setM(this, matrixMultiply(m1, this.m_), true); | |
}; | |
contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) { | |
var m = [ | |
[m11, m12, 0], | |
[m21, m22, 0], | |
[dx, dy, 1] | |
]; | |
setM(this, m, true); | |
}; | |
/** | |
* The text drawing function. | |
* The maxWidth argument isn't taken in account, since no browser supports | |
* it yet. | |
*/ | |
contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) { | |
var m = this.m_, | |
delta = 1000, | |
left = 0, | |
right = delta, | |
offset = {x: 0, y: 0}, | |
lineStr = []; | |
var fontStyle = getComputedStyle(processFontStyle(this.font), | |
this.element_); | |
var fontStyleString = buildStyle(fontStyle); | |
var elementStyle = this.element_.currentStyle; | |
var textAlign = this.textAlign.toLowerCase(); | |
switch (textAlign) { | |
case 'left': | |
case 'center': | |
case 'right': | |
break; | |
case 'end': | |
textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left'; | |
break; | |
case 'start': | |
textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left'; | |
break; | |
default: | |
textAlign = 'left'; | |
} | |
// 1.75 is an arbitrary number, as there is no info about the text baseline | |
switch (this.textBaseline) { | |
case 'hanging': | |
case 'top': | |
offset.y = fontStyle.size / 1.75; | |
break; | |
case 'middle': | |
break; | |
default: | |
case null: | |
case 'alphabetic': | |
case 'ideographic': | |
case 'bottom': | |
offset.y = -fontStyle.size / 2.25; | |
break; | |
} | |
switch(textAlign) { | |
case 'right': | |
left = delta; | |
right = 0.05; | |
break; | |
case 'center': | |
left = right = delta / 2; | |
break; | |
} | |
var d = this.getCoords_(x + offset.x, y + offset.y); | |
lineStr.push('<g_vml_:line from="', -left ,' 0" to="', right ,' 0.05" ', | |
' coordsize="100 100" coordorigin="0 0"', | |
' filled="', !stroke, '" stroked="', !!stroke, | |
'" style="position:absolute;width:1px;height:1px;">'); | |
if (stroke) { | |
appendStroke(this, lineStr); | |
} else { | |
// TODO: Fix the min and max params. | |
appendFill(this, lineStr, {x: -left, y: 0}, | |
{x: right, y: fontStyle.size}); | |
} | |
var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' + | |
m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0'; | |
var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z); | |
lineStr.push('<g_vml_:skew on="t" matrix="', skewM ,'" ', | |
' offset="', skewOffset, '" origin="', left ,' 0" />', | |
'<g_vml_:path textpathok="true" />', | |
'<g_vml_:textpath on="true" string="', | |
encodeHtmlAttribute(text), | |
'" style="v-text-align:', textAlign, | |
';font:', encodeHtmlAttribute(fontStyleString), | |
'" /></g_vml_:line>'); | |
this.element_.insertAdjacentHTML('beforeEnd', lineStr.join('')); | |
}; | |
contextPrototype.fillText = function(text, x, y, maxWidth) { | |
this.drawText_(text, x, y, maxWidth, false); | |
}; | |
contextPrototype.strokeText = function(text, x, y, maxWidth) { | |
this.drawText_(text, x, y, maxWidth, true); | |
}; | |
contextPrototype.measureText = function(text) { | |
if (!this.textMeasureEl_) { | |
var s = '<span style="position:absolute;' + | |
'top:-20000px;left:0;padding:0;margin:0;border:none;' + | |
'white-space:pre;"></span>'; | |
this.element_.insertAdjacentHTML('beforeEnd', s); | |
this.textMeasureEl_ = this.element_.lastChild; | |
} | |
var doc = this.element_.ownerDocument; | |
this.textMeasureEl_.innerHTML = ''; | |
this.textMeasureEl_.style.font = this.font; | |
// Don't use innerHTML or innerText because they allow markup/whitespace. | |
this.textMeasureEl_.appendChild(doc.createTextNode(text)); | |
return {width: this.textMeasureEl_.offsetWidth}; | |
}; | |
/******** STUBS ********/ | |
contextPrototype.clip = function() { | |
// TODO: Implement | |
}; | |
contextPrototype.arcTo = function() { | |
// TODO: Implement | |
}; | |
contextPrototype.createPattern = function(image, repetition) { | |
return new CanvasPattern_(image, repetition); | |
}; | |
// Gradient / Pattern Stubs | |
function CanvasGradient_(aType) { | |
this.type_ = aType; | |
this.x0_ = 0; | |
this.y0_ = 0; | |
this.r0_ = 0; | |
this.x1_ = 0; | |
this.y1_ = 0; | |
this.r1_ = 0; | |
this.colors_ = []; | |
} | |
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) { | |
aColor = processStyle(aColor); | |
this.colors_.push({offset: aOffset, | |
color: aColor.color, | |
alpha: aColor.alpha}); | |
}; | |
function CanvasPattern_(image, repetition) { | |
assertImageIsValid(image); | |
switch (repetition) { | |
case 'repeat': | |
case null: | |
case '': | |
this.repetition_ = 'repeat'; | |
break | |
case 'repeat-x': | |
case 'repeat-y': | |
case 'no-repeat': | |
this.repetition_ = repetition; | |
break; | |
default: | |
throwException('SYNTAX_ERR'); | |
} | |
this.src_ = image.src; | |
this.width_ = image.width; | |
this.height_ = image.height; | |
} | |
function throwException(s) { | |
throw new DOMException_(s); | |
} | |
function assertImageIsValid(img) { | |
if (!img || img.nodeType != 1 || img.tagName != 'IMG') { | |
throwException('TYPE_MISMATCH_ERR'); | |
} | |
if (img.readyState != 'complete') { | |
throwException('INVALID_STATE_ERR'); | |
} | |
} | |
function DOMException_(s) { | |
this.code = this[s]; | |
this.message = s +': DOM Exception ' + this.code; | |
} | |
var p = DOMException_.prototype = new Error; | |
p.INDEX_SIZE_ERR = 1; | |
p.DOMSTRING_SIZE_ERR = 2; | |
p.HIERARCHY_REQUEST_ERR = 3; | |
p.WRONG_DOCUMENT_ERR = 4; | |
p.INVALID_CHARACTER_ERR = 5; | |
p.NO_DATA_ALLOWED_ERR = 6; | |
p.NO_MODIFICATION_ALLOWED_ERR = 7; | |
p.NOT_FOUND_ERR = 8; | |
p.NOT_SUPPORTED_ERR = 9; | |
p.INUSE_ATTRIBUTE_ERR = 10; | |
p.INVALID_STATE_ERR = 11; | |
p.SYNTAX_ERR = 12; | |
p.INVALID_MODIFICATION_ERR = 13; | |
p.NAMESPACE_ERR = 14; | |
p.INVALID_ACCESS_ERR = 15; | |
p.VALIDATION_ERR = 16; | |
p.TYPE_MISMATCH_ERR = 17; | |
// set up externs | |
G_vmlCanvasManager = G_vmlCanvasManager_; | |
CanvasRenderingContext2D = CanvasRenderingContext2D_; | |
CanvasGradient = CanvasGradient_; | |
CanvasPattern = CanvasPattern_; | |
DOMException = DOMException_; | |
})(); | |
} // if | |
if(!document.createElement("canvas").getContext){(function(){var z=Math;var K=z.round;var J=z.sin;var U=z.cos;var b=z.abs;var k=z.sqrt;var D=10;var F=D/2;function T(){return this.context_||(this.context_=new W(this))}var O=Array.prototype.slice;function G(i,j,m){var Z=O.call(arguments,2);return function(){return i.apply(j,Z.concat(O.call(arguments)))}}function AD(Z){return String(Z).replace(/&/g,"&").replace(/"/g,""")}function r(i){if(!i.namespaces.g_vml_){i.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml","#default#VML")}if(!i.namespaces.g_o_){i.namespaces.add("g_o_","urn:schemas-microsoft-com:office:office","#default#VML")}if(!i.styleSheets.ex_canvas_){var Z=i.createStyleSheet();Z.owningElement.id="ex_canvas_";Z.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}"}}r(document);var E={init:function(Z){if(/MSIE/.test(navigator.userAgent)&&!window.opera){var i=Z||document;i.createElement("canvas");i.attachEvent("onreadystatechange",G(this.init_,this,i))}},init_:function(m){var j=m.getElementsByTagName("canvas");for(var Z=0;Z<j.length;Z++){this.initElement(j[Z])}},initElement:function(i){if(!i.getContext){i.getContext=T;r(i.ownerDocument);i.innerHTML="";i.attachEvent("onpropertychange",S);i.attachEvent("onresize",w);var Z=i.attributes;if(Z.width&&Z.width.specified){i.style.width=Z.width.nodeValue+"px"}else{i.width=i.clientWidth}if(Z.height&&Z.height.specified){i.style.height=Z.height.nodeValue+"px"}else{i.height=i.clientHeight}}return i}};function S(i){var Z=i.srcElement;switch(i.propertyName){case"width":Z.getContext().clearRect();Z.style.width=Z.attributes.width.nodeValue+"px";Z.firstChild.style.width=Z.clientWidth+"px";break;case"height":Z.getContext().clearRect();Z.style.height=Z.attributes.height.nodeValue+"px";Z.firstChild.style.height=Z.clientHeight+"px";break}}function w(i){var Z=i.srcElement;if(Z.firstChild){Z.firstChild.style.width=Z.clientWidth+"px";Z.firstChild.style.height=Z.clientHeight+"px"}}E.init();var I=[];for(var AC=0;AC<16;AC++){for(var AB=0;AB<16;AB++){I[AC*16+AB]=AC.toString(16)+AB.toString(16)}}function V(){return[[1,0,0],[0,1,0],[0,0,1]]}function d(m,j){var i=V();for(var Z=0;Z<3;Z++){for(var AF=0;AF<3;AF++){var p=0;for(var AE=0;AE<3;AE++){p+=m[Z][AE]*j[AE][AF]}i[Z][AF]=p}}return i}function Q(i,Z){Z.fillStyle=i.fillStyle;Z.lineCap=i.lineCap;Z.lineJoin=i.lineJoin;Z.lineWidth=i.lineWidth;Z.miterLimit=i.miterLimit;Z.shadowBlur=i.shadowBlur;Z.shadowColor=i.shadowColor;Z.shadowOffsetX=i.shadowOffsetX;Z.shadowOffsetY=i.shadowOffsetY;Z.strokeStyle=i.strokeStyle;Z.globalAlpha=i.globalAlpha;Z.font=i.font;Z.textAlign=i.textAlign;Z.textBaseline=i.textBaseline;Z.arcScaleX_=i.arcScaleX_;Z.arcScaleY_=i.arcScaleY_;Z.lineScale_=i.lineScale_}var B={aliceblue:"#F0F8FF",antiquewhite:"#FAEBD7",aquamarine:"#7FFFD4",azure:"#F0FFFF",beige:"#F5F5DC",bisque:"#FFE4C4",black:"#000000",blanchedalmond:"#FFEBCD",blueviolet:"#8A2BE2",brown:"#A52A2A",burlywood:"#DEB887",cadetblue:"#5F9EA0",chartreuse:"#7FFF00",chocolate:"#D2691E",coral:"#FF7F50",cornflowerblue:"#6495ED",cornsilk:"#FFF8DC",crimson:"#DC143C",cyan:"#00FFFF",darkblue:"#00008B",darkcyan:"#008B8B",darkgoldenrod:"#B8860B",darkgray:"#A9A9A9",darkgreen:"#006400",darkgrey:"#A9A9A9",darkkhaki:"#BDB76B",darkmagenta:"#8B008B",darkolivegreen:"#556B2F",darkorange:"#FF8C00",darkorchid:"#9932CC",darkred:"#8B0000",darksalmon:"#E9967A",darkseagreen:"#8FBC8F",darkslateblue:"#483D8B",darkslategray:"#2F4F4F",darkslategrey:"#2F4F4F",darkturquoise:"#00CED1",darkviolet:"#9400D3",deeppink:"#FF1493",deepskyblue:"#00BFFF",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1E90FF",firebrick:"#B22222",floralwhite:"#FFFAF0",forestgreen:"#228B22",gainsboro:"#DCDCDC",ghostwhite:"#F8F8FF",gold:"#FFD700",goldenrod:"#DAA520",grey:"#808080",greenyellow:"#ADFF2F",honeydew:"#F0FFF0",hotpink:"#FF69B4",indianred:"#CD5C5C",indigo:"#4B0082",ivory:"#FFFFF0",khaki:"#F0E68C",lavender:"#E6E6FA",lavenderblush:"#FFF0F5",lawngreen:"#7CFC00",lemonchiffon:"#FFFACD",lightblue:"#ADD8E6",lightcoral:"#F08080",lightcyan:"#E0FFFF",lightgoldenrodyellow:"#FAFAD2",lightgreen:"#90EE90",lightgrey:"#D3D3D3",lightpink:"#FFB6C1",lightsalmon:"#FFA07A",lightseagreen:"#20B2AA",lightskyblue:"#87CEFA",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#B0C4DE",lightyellow:"#FFFFE0",limegreen:"#32CD32",linen:"#FAF0E6",magenta:"#FF00FF",mediumaquamarine:"#66CDAA",mediumblue:"#0000CD",mediumorchid:"#BA55D3",mediumpurple:"#9370DB",mediumseagreen:"#3CB371",mediumslateblue:"#7B68EE",mediumspringgreen:"#00FA9A",mediumturquoise:"#48D1CC",mediumvioletred:"#C71585",midnightblue:"#191970",mintcream:"#F5FFFA",mistyrose:"#FFE4E1",moccasin:"#FFE4B5",navajowhite:"#FFDEAD",oldlace:"#FDF5E6",olivedrab:"#6B8E23",orange:"#FFA500",orangered:"#FF4500",orchid:"#DA70D6",palegoldenrod:"#EEE8AA",palegreen:"#98FB98",paleturquoise:"#AFEEEE",palevioletred:"#DB7093",papayawhip:"#FFEFD5",peachpuff:"#FFDAB9",peru:"#CD853F",pink:"#FFC0CB",plum:"#DDA0DD",powderblue:"#B0E0E6",rosybrown:"#BC8F8F",royalblue:"#4169E1",saddlebrown:"#8B4513",salmon:"#FA8072",sandybrown:"#F4A460",seagreen:"#2E8B57",seashell:"#FFF5EE",sienna:"#A0522D",skyblue:"#87CEEB",slateblue:"#6A5ACD",slategray:"#708090",slategrey:"#708090",snow:"#FFFAFA",springgreen:"#00FF7F",steelblue:"#4682B4",tan:"#D2B48C",thistle:"#D8BFD8",tomato:"#FF6347",turquoise:"#40E0D0",violet:"#EE82EE",wheat:"#F5DEB3",whitesmoke:"#F5F5F5",yellowgreen:"#9ACD32"};function g(i){var m=i.indexOf("(",3);var Z=i.indexOf(")",m+1);var j=i.substring(m+1,Z).split(",");if(j.length==4&&i.substr(3,1)=="a"){alpha=Number(j[3])}else{j[3]=1}return j}function C(Z){return parseFloat(Z)/100}function N(i,j,Z){return Math.min(Z,Math.max(j,i))}function c(AF){var j,i,Z;h=parseFloat(AF[0])/360%360;if(h<0){h++}s=N(C(AF[1]),0,1);l=N(C(AF[2]),0,1);if(s==0){j=i=Z=l}else{var m=l<0.5?l*(1+s):l+s-l*s;var AE=2*l-m;j=A(AE,m,h+1/3);i=A(AE,m,h);Z=A(AE,m,h-1/3)}return"#"+I[Math.floor(j*255)]+I[Math.floor(i*255)]+I[Math.floor(Z*255)]}function A(i,Z,j){if(j<0){j++}if(j>1){j--}if(6*j<1){return i+(Z-i)*6*j}else{if(2*j<1){return Z}else{if(3*j<2){return i+(Z-i)*(2/3-j)*6}else{return i}}}}function Y(Z){var AE,p=1;Z=String(Z);if(Z.charAt(0)=="#"){AE=Z}else{if(/^rgb/.test(Z)){var m=g(Z);var AE="#",AF;for(var j=0;j<3;j++){if(m[j].indexOf("%")!=-1){AF=Math.floor(C(m[j])*255)}else{AF=Number(m[j])}AE+=I[N(AF,0,255)]}p=m[3]}else{if(/^hsl/.test(Z)){var m=g(Z);AE=c(m);p=m[3]}else{AE=B[Z]||Z}}}return{color:AE,alpha:p}}var L={style:"normal",variant:"normal",weight:"normal",size:10,family:"sans-serif"};var f={};function X(Z){if(f[Z]){return f[Z]}var m=document.createElement("div");var j=m.style;try{j.font=Z}catch(i){}return f[Z]={style:j.fontStyle||L.style,variant:j.fontVariant||L.variant,weight:j.fontWeight||L.weight,size:j.fontSize||L.size,family:j.fontFamily||L.family}}function P(j,i){var Z={};for(var AF in j){Z[AF]=j[AF]}var AE=parseFloat(i.currentStyle.fontSize),m=parseFloat(j.size);if(typeof j.size=="number"){Z.size=j.size}else{if(j.size.indexOf("px")!=-1){Z.size=m}else{if(j.size.indexOf("em")!=-1){Z.size=AE*m}else{if(j.size.indexOf("%")!=-1){Z.size=(AE/100)*m}else{if(j.size.indexOf("pt")!=-1){Z.size=m/0.75}else{Z.size=AE}}}}}Z.size*=0.981;return Z}function AA(Z){return Z.style+" "+Z.variant+" "+Z.weight+" "+Z.size+"px "+Z.family}function t(Z){switch(Z){case"butt":return"flat";case"round":return"round";case"square":default:return"square"}}function W(i){this.m_=V();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=D*1;this.globalAlpha=1;this.font="10px sans-serif";this.textAlign="left";this.textBaseline="alphabetic";this.canvas=i;var Z=i.ownerDocument.createElement("div");Z.style.width=i.clientWidth+"px";Z.style.height=i.clientHeight+"px";Z.style.overflow="hidden";Z.style.position="absolute";i.appendChild(Z);this.element_=Z;this.arcScaleX_=1;this.arcScaleY_=1;this.lineScale_=1}var M=W.prototype;M.clearRect=function(){if(this.textMeasureEl_){this.textMeasureEl_.removeNode(true);this.textMeasureEl_=null}this.element_.innerHTML=""};M.beginPath=function(){this.currentPath_=[]};M.moveTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"moveTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.lineTo=function(i,Z){var j=this.getCoords_(i,Z);this.currentPath_.push({type:"lineTo",x:j.x,y:j.y});this.currentX_=j.x;this.currentY_=j.y};M.bezierCurveTo=function(j,i,AI,AH,AG,AE){var Z=this.getCoords_(AG,AE);var AF=this.getCoords_(j,i);var m=this.getCoords_(AI,AH);e(this,AF,m,Z)};function e(Z,m,j,i){Z.currentPath_.push({type:"bezierCurveTo",cp1x:m.x,cp1y:m.y,cp2x:j.x,cp2y:j.y,x:i.x,y:i.y});Z.currentX_=i.x;Z.currentY_=i.y}M.quadraticCurveTo=function(AG,j,i,Z){var AF=this.getCoords_(AG,j);var AE=this.getCoords_(i,Z);var AH={x:this.currentX_+2/3*(AF.x-this.currentX_),y:this.currentY_+2/3*(AF.y-this.currentY_)};var m={x:AH.x+(AE.x-this.currentX_)/3,y:AH.y+(AE.y-this.currentY_)/3};e(this,AH,m,AE)};M.arc=function(AJ,AH,AI,AE,i,j){AI*=D;var AN=j?"at":"wa";var AK=AJ+U(AE)*AI-F;var AM=AH+J(AE)*AI-F;var Z=AJ+U(i)*AI-F;var AL=AH+J(i)*AI-F;if(AK==Z&&!j){AK+=0.125}var m=this.getCoords_(AJ,AH);var AG=this.getCoords_(AK,AM);var AF=this.getCoords_(Z,AL);this.currentPath_.push({type:AN,x:m.x,y:m.y,radius:AI,xStart:AG.x,yStart:AG.y,xEnd:AF.x,yEnd:AF.y})};M.rect=function(j,i,Z,m){this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath()};M.strokeRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.stroke();this.currentPath_=p};M.fillRect=function(j,i,Z,m){var p=this.currentPath_;this.beginPath();this.moveTo(j,i);this.lineTo(j+Z,i);this.lineTo(j+Z,i+m);this.lineTo(j,i+m);this.closePath();this.fill();this.currentPath_=p};M.createLinearGradient=function(i,m,Z,j){var p=new v("gradient");p.x0_=i;p.y0_=m;p.x1_=Z;p.y1_=j;return p};M.createRadialGradient=function(m,AE,j,i,p,Z){var AF=new v("gradientradial");AF.x0_=m;AF.y0_=AE;AF.r0_=j;AF.x1_=i;AF.y1_=p;AF.r1_=Z;return AF};M.drawImage=function(AO,j){var AH,AF,AJ,AV,AM,AK,AQ,AX;var AI=AO.runtimeStyle.width;var AN=AO.runtimeStyle.height;AO.runtimeStyle.width="auto";AO.runtimeStyle.height="auto";var AG=AO.width;var AT=AO.height;AO.runtimeStyle.width=AI;AO.runtimeStyle.height=AN;if(arguments.length==3){AH=arguments[1];AF=arguments[2];AM=AK=0;AQ=AJ=AG;AX=AV=AT}else{if(arguments.length==5){AH=arguments[1];AF=arguments[2];AJ=arguments[3];AV=arguments[4];AM=AK=0;AQ=AG;AX=AT}else{if(arguments.length==9){AM=arguments[1];AK=arguments[2];AQ=arguments[3];AX=arguments[4];AH=arguments[5];AF=arguments[6];AJ=arguments[7];AV=arguments[8]}else{throw Error("Invalid number of arguments")}}}var AW=this.getCoords_(AH,AF);var m=AQ/2;var i=AX/2;var AU=[];var Z=10;var AE=10;AU.push(" <g_vml_:group",' coordsize="',D*Z,",",D*AE,'"',' coordorigin="0,0"',' style="width:',Z,"px;height:",AE,"px;position:absolute;");if(this.m_[0][0]!=1||this.m_[0][1]||this.m_[1][1]!=1||this.m_[1][0]){var p=[];p.push("M11=",this.m_[0][0],",","M12=",this.m_[1][0],",","M21=",this.m_[0][1],",","M22=",this.m_[1][1],",","Dx=",K(AW.x/D),",","Dy=",K(AW.y/D),"");var AS=AW;var AR=this.getCoords_(AH+AJ,AF);var AP=this.getCoords_(AH,AF+AV);var AL=this.getCoords_(AH+AJ,AF+AV);AS.x=z.max(AS.x,AR.x,AP.x,AL.x);AS.y=z.max(AS.y,AR.y,AP.y,AL.y);AU.push("padding:0 ",K(AS.x/D),"px ",K(AS.y/D),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",p.join(""),", sizingmethod='clip');")}else{AU.push("top:",K(AW.y/D),"px;left:",K(AW.x/D),"px;")}AU.push(' ">','<g_vml_:image src="',AO.src,'"',' style="width:',D*AJ,"px;"," height:",D*AV,'px"',' cropleft="',AM/AG,'"',' croptop="',AK/AT,'"',' cropright="',(AG-AM-AQ)/AG,'"',' cropbottom="',(AT-AK-AX)/AT,'"'," />","</g_vml_:group>");this.element_.insertAdjacentHTML("BeforeEnd",AU.join(""))};M.stroke=function(AM){var m=10;var AN=10;var AE=5000;var AG={x:null,y:null};var AL={x:null,y:null};for(var AH=0;AH<this.currentPath_.length;AH+=AE){var AK=[];var AF=false;AK.push("<g_vml_:shape",' filled="',!!AM,'"',' style="position:absolute;width:',m,"px;height:",AN,'px;"',' coordorigin="0,0"',' coordsize="',D*m,",",D*AN,'"',' stroked="',!AM,'"',' path="');var AO=false;for(var AI=AH;AI<Math.min(AH+AE,this.currentPath_.length);AI++){if(AI%AE==0&&AI>0){AK.push(" m ",K(this.currentPath_[AI-1].x),",",K(this.currentPath_[AI-1].y))}var Z=this.currentPath_[AI];var AJ;switch(Z.type){case"moveTo":AJ=Z;AK.push(" m ",K(Z.x),",",K(Z.y));break;case"lineTo":AK.push(" l ",K(Z.x),",",K(Z.y));break;case"close":AK.push(" x ");Z=null;break;case"bezierCurveTo":AK.push(" c ",K(Z.cp1x),",",K(Z.cp1y),",",K(Z.cp2x),",",K(Z.cp2y),",",K(Z.x),",",K(Z.y));break;case"at":case"wa":AK.push(" ",Z.type," ",K(Z.x-this.arcScaleX_*Z.radius),",",K(Z.y-this.arcScaleY_*Z.radius)," ",K(Z.x+this.arcScaleX_*Z.radius),",",K(Z.y+this.arcScaleY_*Z.radius)," ",K(Z.xStart),",",K(Z.yStart)," ",K(Z.xEnd),",",K(Z.yEnd));break}if(Z){if(AG.x==null||Z.x<AG.x){AG.x=Z.x}if(AL.x==null||Z.x>AL.x){AL.x=Z.x}if(AG.y==null||Z.y<AG.y){AG.y=Z.y}if(AL.y==null||Z.y>AL.y){AL.y=Z.y}}}AK.push(' ">');if(!AM){R(this,AK)}else{a(this,AK,AG,AL)}AK.push("</g_vml_:shape>");this.element_.insertAdjacentHTML("beforeEnd",AK.join(""))}};function R(j,AE){var i=Y(j.strokeStyle);var m=i.color;var p=i.alpha*j.globalAlpha;var Z=j.lineScale_*j.lineWidth;if(Z<1){p*=Z}AE.push("<g_vml_:stroke",' opacity="',p,'"',' joinstyle="',j.lineJoin,'"',' miterlimit="',j.miterLimit,'"',' endcap="',t(j.lineCap),'"',' weight="',Z,'px"',' color="',m,'" />')}function a(AO,AG,Ah,AP){var AH=AO.fillStyle;var AY=AO.arcScaleX_;var AX=AO.arcScaleY_;var Z=AP.x-Ah.x;var m=AP.y-Ah.y;if(AH instanceof v){var AL=0;var Ac={x:0,y:0};var AU=0;var AK=1;if(AH.type_=="gradient"){var AJ=AH.x0_/AY;var j=AH.y0_/AX;var AI=AH.x1_/AY;var Aj=AH.y1_/AX;var Ag=AO.getCoords_(AJ,j);var Af=AO.getCoords_(AI,Aj);var AE=Af.x-Ag.x;var p=Af.y-Ag.y;AL=Math.atan2(AE,p)*180/Math.PI;if(AL<0){AL+=360}if(AL<0.000001){AL=0}}else{var Ag=AO.getCoords_(AH.x0_,AH.y0_);Ac={x:(Ag.x-Ah.x)/Z,y:(Ag.y-Ah.y)/m};Z/=AY*D;m/=AX*D;var Aa=z.max(Z,m);AU=2*AH.r0_/Aa;AK=2*AH.r1_/Aa-AU}var AS=AH.colors_;AS.sort(function(Ak,i){return Ak.offset-i.offset});var AN=AS.length;var AR=AS[0].color;var AQ=AS[AN-1].color;var AW=AS[0].alpha*AO.globalAlpha;var AV=AS[AN-1].alpha*AO.globalAlpha;var Ab=[];for(var Ae=0;Ae<AN;Ae++){var AM=AS[Ae];Ab.push(AM.offset*AK+AU+" "+AM.color)}AG.push('<g_vml_:fill type="',AH.type_,'"',' method="none" focus="100%"',' color="',AR,'"',' color2="',AQ,'"',' colors="',Ab.join(","),'"',' opacity="',AV,'"',' g_o_:opacity2="',AW,'"',' angle="',AL,'"',' focusposition="',Ac.x,",",Ac.y,'" />')}else{if(AH instanceof u){if(Z&&m){var AF=-Ah.x;var AZ=-Ah.y;AG.push("<g_vml_:fill",' position="',AF/Z*AY*AY,",",AZ/m*AX*AX,'"',' type="tile"',' src="',AH.src_,'" />')}}else{var Ai=Y(AO.fillStyle);var AT=Ai.color;var Ad=Ai.alpha*AO.globalAlpha;AG.push('<g_vml_:fill color="',AT,'" opacity="',Ad,'" />')}}}M.fill=function(){this.stroke(true)};M.closePath=function(){this.currentPath_.push({type:"close"})};M.getCoords_=function(j,i){var Z=this.m_;return{x:D*(j*Z[0][0]+i*Z[1][0]+Z[2][0])-F,y:D*(j*Z[0][1]+i*Z[1][1]+Z[2][1])-F}};M.save=function(){var Z={};Q(this,Z);this.aStack_.push(Z);this.mStack_.push(this.m_);this.m_=d(V(),this.m_)};M.restore=function(){if(this.aStack_.length){Q(this.aStack_.pop(),this);this.m_=this.mStack_.pop()}};function H(Z){return isFinite(Z[0][0])&&isFinite(Z[0][1])&&isFinite(Z[1][0])&&isFinite(Z[1][1])&&isFinite(Z[2][0])&&isFinite(Z[2][1])}function y(i,Z,j){if(!H(Z)){return }i.m_=Z;if(j){var p=Z[0][0]*Z[1][1]-Z[0][1]*Z[1][0];i.lineScale_=k(b(p))}}M.translate=function(j,i){var Z=[[1,0,0],[0,1,0],[j,i,1]];y(this,d(Z,this.m_),false)};M.rotate=function(i){var m=U(i);var j=J(i);var Z=[[m,j,0],[-j,m,0],[0,0,1]];y(this,d(Z,this.m_),false)};M.scale=function(j,i){this.arcScaleX_*=j;this.arcScaleY_*=i;var Z=[[j,0,0],[0,i,0],[0,0,1]];y(this,d(Z,this.m_),true)};M.transform=function(p,m,AF,AE,i,Z){var j=[[p,m,0],[AF,AE,0],[i,Z,1]];y(this,d(j,this.m_),true)};M.setTransform=function(AE,p,AG,AF,j,i){var Z=[[AE,p,0],[AG,AF,0],[j,i,1]];y(this,Z,true)};M.drawText_=function(AK,AI,AH,AN,AG){var AM=this.m_,AQ=1000,i=0,AP=AQ,AF={x:0,y:0},AE=[];var Z=P(X(this.font),this.element_);var j=AA(Z);var AR=this.element_.currentStyle;var p=this.textAlign.toLowerCase();switch(p){case"left":case"center":case"right":break;case"end":p=AR.direction=="ltr"?"right":"left";break;case"start":p=AR.direction=="rtl"?"right":"left";break;default:p="left"}switch(this.textBaseline){case"hanging":case"top":AF.y=Z.size/1.75;break;case"middle":break;default:case null:case"alphabetic":case"ideographic":case"bottom":AF.y=-Z.size/2.25;break}switch(p){case"right":i=AQ;AP=0.05;break;case"center":i=AP=AQ/2;break}var AO=this.getCoords_(AI+AF.x,AH+AF.y);AE.push('<g_vml_:line from="',-i,' 0" to="',AP,' 0.05" ',' coordsize="100 100" coordorigin="0 0"',' filled="',!AG,'" stroked="',!!AG,'" style="position:absolute;width:1px;height:1px;">');if(AG){R(this,AE)}else{a(this,AE,{x:-i,y:0},{x:AP,y:Z.size})}var AL=AM[0][0].toFixed(3)+","+AM[1][0].toFixed(3)+","+AM[0][1].toFixed(3)+","+AM[1][1].toFixed(3)+",0,0";var AJ=K(AO.x/D)+","+K(AO.y/D);AE.push('<g_vml_:skew on="t" matrix="',AL,'" ',' offset="',AJ,'" origin="',i,' 0" />','<g_vml_:path textpathok="true" />','<g_vml_:textpath on="true" string="',AD(AK),'" style="v-text-align:',p,";font:",AD(j),'" /></g_vml_:line>');this.element_.insertAdjacentHTML("beforeEnd",AE.join(""))};M.fillText=function(j,Z,m,i){this.drawText_(j,Z,m,i,false)};M.strokeText=function(j,Z,m,i){this.drawText_(j,Z,m,i,true)};M.measureText=function(j){if(!this.textMeasureEl_){var Z='<span style="position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;"></span>';this.element_.insertAdjacentHTML("beforeEnd",Z);this.textMeasureEl_=this.element_.lastChild}var i=this.element_.ownerDocument;this.textMeasureEl_.innerHTML="";this.textMeasureEl_.style.font=this.font;this.textMeasureEl_.appendChild(i.createTextNode(j));return{width:this.textMeasureEl_.offsetWidth}};M.clip=function(){};M.arcTo=function(){};M.createPattern=function(i,Z){return new u(i,Z)};function v(Z){this.type_=Z;this.x0_=0;this.y0_=0;this.r0_=0;this.x1_=0;this.y1_=0;this.r1_=0;this.colors_=[]}v.prototype.addColorStop=function(i,Z){Z=Y(Z);this.colors_.push({offset:i,color:Z.color,alpha:Z.alpha})};function u(i,Z){q(i);switch(Z){case"repeat":case null:case"":this.repetition_="repeat";break;case"repeat-x":case"repeat-y":case"no-repeat":this.repetition_=Z;break;default:n("SYNTAX_ERR")}this.src_=i.src;this.width_=i.width;this.height_=i.height}function n(Z){throw new o(Z)}function q(Z){if(!Z||Z.nodeType!=1||Z.tagName!="IMG"){n("TYPE_MISMATCH_ERR")}if(Z.readyState!="complete"){n("INVALID_STATE_ERR")}}function o(Z){this.code=this[Z];this.message=Z+": DOM Exception "+this.code}var x=o.prototype=new Error;x.INDEX_SIZE_ERR=1;x.DOMSTRING_SIZE_ERR=2;x.HIERARCHY_REQUEST_ERR=3;x.WRONG_DOCUMENT_ERR=4;x.INVALID_CHARACTER_ERR=5;x.NO_DATA_ALLOWED_ERR=6;x.NO_MODIFICATION_ALLOWED_ERR=7;x.NOT_FOUND_ERR=8;x.NOT_SUPPORTED_ERR=9;x.INUSE_ATTRIBUTE_ERR=10;x.INVALID_STATE_ERR=11;x.SYNTAX_ERR=12;x.INVALID_MODIFICATION_ERR=13;x.NAMESPACE_ERR=14;x.INVALID_ACCESS_ERR=15;x.VALIDATION_ERR=16;x.TYPE_MISMATCH_ERR=17;G_vmlCanvasManager=E;CanvasRenderingContext2D=W;CanvasGradient=v;CanvasPattern=u;DOMException=o})()}; |
/* Plugin for jQuery for working with colors. | |
* | |
* Version 1.1. | |
* | |
* Inspiration from jQuery color animation plugin by John Resig. | |
* | |
* Released under the MIT license by Ole Laursen, October 2009. | |
* | |
* Examples: | |
* | |
* $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() | |
* var c = $.color.extract($("#mydiv"), 'background-color'); | |
* console.log(c.r, c.g, c.b, c.a); | |
* $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" | |
* | |
* Note that .scale() and .add() return the same modified object | |
* instead of making a new one. | |
* | |
* V. 1.1: Fix error handling so e.g. parsing an empty string does | |
* produce a color rather than just crashing. | |
*/ | |
(function($) { | |
$.color = {}; | |
// construct color object with some convenient chainable helpers | |
$.color.make = function (r, g, b, a) { | |
var o = {}; | |
o.r = r || 0; | |
o.g = g || 0; | |
o.b = b || 0; | |
o.a = a != null ? a : 1; | |
o.add = function (c, d) { | |
for (var i = 0; i < c.length; ++i) | |
o[c.charAt(i)] += d; | |
return o.normalize(); | |
}; | |
o.scale = function (c, f) { | |
for (var i = 0; i < c.length; ++i) | |
o[c.charAt(i)] *= f; | |
return o.normalize(); | |
}; | |
o.toString = function () { | |
if (o.a >= 1.0) { | |
return "rgb("+[o.r, o.g, o.b].join(",")+")"; | |
} else { | |
return "rgba("+[o.r, o.g, o.b, o.a].join(",")+")"; | |
} | |
}; | |
o.normalize = function () { | |
function clamp(min, value, max) { | |
return value < min ? min: (value > max ? max: value); | |
} | |
o.r = clamp(0, parseInt(o.r), 255); | |
o.g = clamp(0, parseInt(o.g), 255); | |
o.b = clamp(0, parseInt(o.b), 255); | |
o.a = clamp(0, o.a, 1); | |
return o; | |
}; | |
o.clone = function () { | |
return $.color.make(o.r, o.b, o.g, o.a); | |
}; | |
return o.normalize(); | |
} | |
// extract CSS color property from element, going up in the DOM | |
// if it's "transparent" | |
$.color.extract = function (elem, css) { | |
var c; | |
do { | |
c = elem.css(css).toLowerCase(); | |
// keep going until we find an element that has color, or | |
// we hit the body | |
if (c != '' && c != 'transparent') | |
break; | |
elem = elem.parent(); | |
} while (!$.nodeName(elem.get(0), "body")); | |
// catch Safari's way of signalling transparent | |
if (c == "rgba(0, 0, 0, 0)") | |
c = "transparent"; | |
return $.color.parse(c); | |
} | |
// parse CSS color string (like "rgb(10, 32, 43)" or "#fff"), | |
// returns color object, if parsing failed, you get black (0, 0, | |
// 0) out | |
$.color.parse = function (str) { | |
var res, m = $.color.make; | |
// Look for rgb(num,num,num) | |
if (res = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str)) | |
return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10)); | |
// Look for rgba(num,num,num,num) | |
if (res = /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(str)) | |
return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4])); | |
// Look for rgb(num%,num%,num%) | |
if (res = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str)) | |
return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55); | |
// Look for rgba(num%,num%,num%,num) | |
if (res = /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(str)) | |
return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4])); | |
// Look for #a0b1c2 | |
if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str)) | |
return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16)); | |
// Look for #fff | |
if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str)) | |
return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16)); | |
// Otherwise, we're most likely dealing with a named color | |
var name = $.trim(str).toLowerCase(); | |
if (name == "transparent") | |
return m(255, 255, 255, 0); | |
else { | |
// default to black | |
res = lookupColors[name] || [0, 0, 0]; | |
return m(res[0], res[1], res[2]); | |
} | |
} | |
var lookupColors = { | |
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] | |
}; | |
})(jQuery); | |
(function(b){b.color={};b.color.make=function(f,e,c,d){var h={};h.r=f||0;h.g=e||0;h.b=c||0;h.a=d!=null?d:1;h.add=function(k,j){for(var g=0;g<k.length;++g){h[k.charAt(g)]+=j}return h.normalize()};h.scale=function(k,j){for(var g=0;g<k.length;++g){h[k.charAt(g)]*=j}return h.normalize()};h.toString=function(){if(h.a>=1){return"rgb("+[h.r,h.g,h.b].join(",")+")"}else{return"rgba("+[h.r,h.g,h.b,h.a].join(",")+")"}};h.normalize=function(){function g(j,k,i){return k<j?j:(k>i?i:k)}h.r=g(0,parseInt(h.r),255);h.g=g(0,parseInt(h.g),255);h.b=g(0,parseInt(h.b),255);h.a=g(0,h.a,1);return h};h.clone=function(){return b.color.make(h.r,h.b,h.g,h.a)};return h.normalize()};b.color.extract=function(e,d){var f;do{f=e.css(d).toLowerCase();if(f!=""&&f!="transparent"){break}e=e.parent()}while(!b.nodeName(e.get(0),"body"));if(f=="rgba(0, 0, 0, 0)"){f="transparent"}return b.color.parse(f)};b.color.parse=function(f){var e,c=b.color.make;if(e=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(f)){return c(parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10))}if(e=/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(f)){return c(parseInt(e[1],10),parseInt(e[2],10),parseInt(e[3],10),parseFloat(e[4]))}if(e=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(f)){return c(parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55)}if(e=/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(f)){return c(parseFloat(e[1])*2.55,parseFloat(e[2])*2.55,parseFloat(e[3])*2.55,parseFloat(e[4]))}if(e=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(f)){return c(parseInt(e[1],16),parseInt(e[2],16),parseInt(e[3],16))}if(e=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(f)){return c(parseInt(e[1]+e[1],16),parseInt(e[2]+e[2],16),parseInt(e[3]+e[3],16))}var d=b.trim(f).toLowerCase();if(d=="transparent"){return c(255,255,255,0)}else{e=a[d]||[0,0,0];return c(e[0],e[1],e[2])}};var a={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]}})(jQuery); |
/* | |
Flot plugin for showing crosshairs, thin lines, when the mouse hovers | |
over the plot. | |
crosshair: { | |
mode: null or "x" or "y" or "xy" | |
color: color | |
lineWidth: number | |
} | |
Set the mode to one of "x", "y" or "xy". The "x" mode enables a | |
vertical crosshair that lets you trace the values on the x axis, "y" | |
enables a horizontal crosshair and "xy" enables them both. "color" is | |
the color of the crosshair (default is "rgba(170, 0, 0, 0.80)"), | |
"lineWidth" is the width of the drawn lines (default is 1). | |
The plugin also adds four public methods: | |
- setCrosshair(pos) | |
Set the position of the crosshair. Note that this is cleared if | |
the user moves the mouse. "pos" is in coordinates of the plot and | |
should be on the form { x: xpos, y: ypos } (you can use x2/x3/... | |
if you're using multiple axes), which is coincidentally the same | |
format as what you get from a "plothover" event. If "pos" is null, | |
the crosshair is cleared. | |
- clearCrosshair() | |
Clear the crosshair. | |
- lockCrosshair(pos) | |
Cause the crosshair to lock to the current location, no longer | |
updating if the user moves the mouse. Optionally supply a position | |
(passed on to setCrosshair()) to move it to. | |
Example usage: | |
var myFlot = $.plot( $("#graph"), ..., { crosshair: { mode: "x" } } }; | |
$("#graph").bind("plothover", function (evt, position, item) { | |
if (item) { | |
// Lock the crosshair to the data point being hovered | |
myFlot.lockCrosshair({ x: item.datapoint[0], y: item.datapoint[1] }); | |
} | |
else { | |
// Return normal crosshair operation | |
myFlot.unlockCrosshair(); | |
} | |
}); | |
- unlockCrosshair() | |
Free the crosshair to move again after locking it. | |
*/ | |
(function ($) { | |
var options = { | |
crosshair: { | |
mode: null, // one of null, "x", "y" or "xy", | |
color: "rgba(170, 0, 0, 0.80)", | |
lineWidth: 1 | |
} | |
}; | |
function init(plot) { | |
// position of crosshair in pixels | |
var crosshair = { x: -1, y: -1, locked: false }; | |
plot.setCrosshair = function setCrosshair(pos) { | |
if (!pos) | |
crosshair.x = -1; | |
else { | |
var o = plot.p2c(pos); | |
crosshair.x = Math.max(0, Math.min(o.left, plot.width())); | |
crosshair.y = Math.max(0, Math.min(o.top, plot.height())); | |
} | |
plot.triggerRedrawOverlay(); | |
}; | |
plot.clearCrosshair = plot.setCrosshair; // passes null for pos | |
plot.lockCrosshair = function lockCrosshair(pos) { | |
if (pos) | |
plot.setCrosshair(pos); | |
crosshair.locked = true; | |
} | |
plot.unlockCrosshair = function unlockCrosshair() { | |
crosshair.locked = false; | |
} | |
function onMouseOut(e) { | |
if (crosshair.locked) | |
return; | |
if (crosshair.x != -1) { | |
crosshair.x = -1; | |
plot.triggerRedrawOverlay(); | |
} | |
} | |
function onMouseMove(e) { | |
if (crosshair.locked) | |
return; | |
if (plot.getSelection && plot.getSelection()) { | |
crosshair.x = -1; // hide the crosshair while selecting | |
return; | |
} | |
var offset = plot.offset(); | |
crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width())); | |
crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height())); | |
plot.triggerRedrawOverlay(); | |
} | |
plot.hooks.bindEvents.push(function (plot, eventHolder) { | |
if (!plot.getOptions().crosshair.mode) | |
return; | |
eventHolder.mouseout(onMouseOut); | |
eventHolder.mousemove(onMouseMove); | |
}); | |
plot.hooks.drawOverlay.push(function (plot, ctx) { | |
var c = plot.getOptions().crosshair; | |
if (!c.mode) | |
return; | |
var plotOffset = plot.getPlotOffset(); | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
if (crosshair.x != -1) { | |
ctx.strokeStyle = c.color; | |
ctx.lineWidth = c.lineWidth; | |
ctx.lineJoin = "round"; | |
ctx.beginPath(); | |
if (c.mode.indexOf("x") != -1) { | |
ctx.moveTo(crosshair.x, 0); | |
ctx.lineTo(crosshair.x, plot.height()); | |
} | |
if (c.mode.indexOf("y") != -1) { | |
ctx.moveTo(0, crosshair.y); | |
ctx.lineTo(plot.width(), crosshair.y); | |
} | |
ctx.stroke(); | |
} | |
ctx.restore(); | |
}); | |
plot.hooks.shutdown.push(function (plot, eventHolder) { | |
eventHolder.unbind("mouseout", onMouseOut); | |
eventHolder.unbind("mousemove", onMouseMove); | |
}); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'crosshair', | |
version: '1.0' | |
}); | |
})(jQuery); | |
(function(b){var a={crosshair:{mode:null,color:"rgba(170, 0, 0, 0.80)",lineWidth:1}};function c(h){var j={x:-1,y:-1,locked:false};h.setCrosshair=function e(l){if(!l){j.x=-1}else{var k=h.p2c(l);j.x=Math.max(0,Math.min(k.left,h.width()));j.y=Math.max(0,Math.min(k.top,h.height()))}h.triggerRedrawOverlay()};h.clearCrosshair=h.setCrosshair;h.lockCrosshair=function f(k){if(k){h.setCrosshair(k)}j.locked=true};h.unlockCrosshair=function g(){j.locked=false};function d(k){if(j.locked){return}if(j.x!=-1){j.x=-1;h.triggerRedrawOverlay()}}function i(k){if(j.locked){return}if(h.getSelection&&h.getSelection()){j.x=-1;return}var l=h.offset();j.x=Math.max(0,Math.min(k.pageX-l.left,h.width()));j.y=Math.max(0,Math.min(k.pageY-l.top,h.height()));h.triggerRedrawOverlay()}h.hooks.bindEvents.push(function(l,k){if(!l.getOptions().crosshair.mode){return}k.mouseout(d);k.mousemove(i)});h.hooks.drawOverlay.push(function(m,k){var n=m.getOptions().crosshair;if(!n.mode){return}var l=m.getPlotOffset();k.save();k.translate(l.left,l.top);if(j.x!=-1){k.strokeStyle=n.color;k.lineWidth=n.lineWidth;k.lineJoin="round";k.beginPath();if(n.mode.indexOf("x")!=-1){k.moveTo(j.x,0);k.lineTo(j.x,m.height())}if(n.mode.indexOf("y")!=-1){k.moveTo(0,j.y);k.lineTo(m.width(),j.y)}k.stroke()}k.restore()});h.hooks.shutdown.push(function(l,k){k.unbind("mouseout",d);k.unbind("mousemove",i)})}b.plot.plugins.push({init:c,options:a,name:"crosshair",version:"1.0"})})(jQuery); |
/* | |
Flot plugin for computing bottoms for filled line and bar charts. | |
The case: you've got two series that you want to fill the area | |
between. In Flot terms, you need to use one as the fill bottom of the | |
other. You can specify the bottom of each data point as the third | |
coordinate manually, or you can use this plugin to compute it for you. | |
In order to name the other series, you need to give it an id, like this | |
var dataset = [ | |
{ data: [ ... ], id: "foo" } , // use default bottom | |
{ data: [ ... ], fillBetween: "foo" }, // use first dataset as bottom | |
]; | |
$.plot($("#placeholder"), dataset, { line: { show: true, fill: true }}); | |
As a convenience, if the id given is a number that doesn't appear as | |
an id in the series, it is interpreted as the index in the array | |
instead (so fillBetween: 0 can also mean the first series). | |
Internally, the plugin modifies the datapoints in each series. For | |
line series, extra data points might be inserted through | |
interpolation. Note that at points where the bottom line is not | |
defined (due to a null point or start/end of line), the current line | |
will show a gap too. The algorithm comes from the jquery.flot.stack.js | |
plugin, possibly some code could be shared. | |
*/ | |
(function ($) { | |
var options = { | |
series: { fillBetween: null } // or number | |
}; | |
function init(plot) { | |
function findBottomSeries(s, allseries) { | |
var i; | |
for (i = 0; i < allseries.length; ++i) { | |
if (allseries[i].id == s.fillBetween) | |
return allseries[i]; | |
} | |
if (typeof s.fillBetween == "number") { | |
i = s.fillBetween; | |
if (i < 0 || i >= allseries.length) | |
return null; | |
return allseries[i]; | |
} | |
return null; | |
} | |
function computeFillBottoms(plot, s, datapoints) { | |
if (s.fillBetween == null) | |
return; | |
var other = findBottomSeries(s, plot.getData()); | |
if (!other) | |
return; | |
var ps = datapoints.pointsize, | |
points = datapoints.points, | |
otherps = other.datapoints.pointsize, | |
otherpoints = other.datapoints.points, | |
newpoints = [], | |
px, py, intery, qx, qy, bottom, | |
withlines = s.lines.show, | |
withbottom = ps > 2 && datapoints.format[2].y, | |
withsteps = withlines && s.lines.steps, | |
fromgap = true, | |
i = 0, j = 0, l; | |
while (true) { | |
if (i >= points.length) | |
break; | |
l = newpoints.length; | |
if (points[i] == null) { | |
// copy gaps | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
i += ps; | |
} | |
else if (j >= otherpoints.length) { | |
// for lines, we can't use the rest of the points | |
if (!withlines) { | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
} | |
i += ps; | |
} | |
else if (otherpoints[j] == null) { | |
// oops, got a gap | |
for (m = 0; m < ps; ++m) | |
newpoints.push(null); | |
fromgap = true; | |
j += otherps; | |
} | |
else { | |
// cases where we actually got two points | |
px = points[i]; | |
py = points[i + 1]; | |
qx = otherpoints[j]; | |
qy = otherpoints[j + 1]; | |
bottom = 0; | |
if (px == qx) { | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
//newpoints[l + 1] += qy; | |
bottom = qy; | |
i += ps; | |
j += otherps; | |
} | |
else if (px > qx) { | |
// we got past point below, might need to | |
// insert interpolated extra point | |
if (withlines && i > 0 && points[i - ps] != null) { | |
intery = py + (points[i - ps + 1] - py) * (qx - px) / (points[i - ps] - px); | |
newpoints.push(qx); | |
newpoints.push(intery) | |
for (m = 2; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
bottom = qy; | |
} | |
j += otherps; | |
} | |
else { // px < qx | |
if (fromgap && withlines) { | |
// if we come from a gap, we just skip this point | |
i += ps; | |
continue; | |
} | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
// we might be able to interpolate a point below, | |
// this can give us a better y | |
if (withlines && j > 0 && otherpoints[j - otherps] != null) | |
bottom = qy + (otherpoints[j - otherps + 1] - qy) * (px - qx) / (otherpoints[j - otherps] - qx); | |
//newpoints[l + 1] += bottom; | |
i += ps; | |
} | |
fromgap = false; | |
if (l != newpoints.length && withbottom) | |
newpoints[l + 2] = bottom; | |
} | |
// maintain the line steps invariant | |
if (withsteps && l != newpoints.length && l > 0 | |
&& newpoints[l] != null | |
&& newpoints[l] != newpoints[l - ps] | |
&& newpoints[l + 1] != newpoints[l - ps + 1]) { | |
for (m = 0; m < ps; ++m) | |
newpoints[l + ps + m] = newpoints[l + m]; | |
newpoints[l + 1] = newpoints[l - ps + 1]; | |
} | |
} | |
datapoints.points = newpoints; | |
} | |
plot.hooks.processDatapoints.push(computeFillBottoms); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'fillbetween', | |
version: '1.0' | |
}); | |
})(jQuery); | |
(function(b){var a={series:{fillBetween:null}};function c(f){function d(j,h){var g;for(g=0;g<h.length;++g){if(h[g].id==j.fillBetween){return h[g]}}if(typeof j.fillBetween=="number"){g=j.fillBetween;if(g<0||g>=h.length){return null}return h[g]}return null}function e(B,u,g){if(u.fillBetween==null){return}var p=d(u,B.getData());if(!p){return}var y=g.pointsize,E=g.points,h=p.datapoints.pointsize,x=p.datapoints.points,r=[],w,v,k,G,F,q,t=u.lines.show,o=y>2&&g.format[2].y,n=t&&u.lines.steps,D=true,C=0,A=0,z;while(true){if(C>=E.length){break}z=r.length;if(E[C]==null){for(m=0;m<y;++m){r.push(E[C+m])}C+=y}else{if(A>=x.length){if(!t){for(m=0;m<y;++m){r.push(E[C+m])}}C+=y}else{if(x[A]==null){for(m=0;m<y;++m){r.push(null)}D=true;A+=h}else{w=E[C];v=E[C+1];G=x[A];F=x[A+1];q=0;if(w==G){for(m=0;m<y;++m){r.push(E[C+m])}q=F;C+=y;A+=h}else{if(w>G){if(t&&C>0&&E[C-y]!=null){k=v+(E[C-y+1]-v)*(G-w)/(E[C-y]-w);r.push(G);r.push(k);for(m=2;m<y;++m){r.push(E[C+m])}q=F}A+=h}else{if(D&&t){C+=y;continue}for(m=0;m<y;++m){r.push(E[C+m])}if(t&&A>0&&x[A-h]!=null){q=F+(x[A-h+1]-F)*(w-G)/(x[A-h]-G)}C+=y}}D=false;if(z!=r.length&&o){r[z+2]=q}}}}if(n&&z!=r.length&&z>0&&r[z]!=null&&r[z]!=r[z-y]&&r[z+1]!=r[z-y+1]){for(m=0;m<y;++m){r[z+y+m]=r[z+m]}r[z+1]=r[z-y+1]}}g.points=r}f.hooks.processDatapoints.push(e)}b.plot.plugins.push({init:c,options:a,name:"fillbetween",version:"1.0"})})(jQuery); |
/* | |
Flot plugin for plotting images, e.g. useful for putting ticks on a | |
prerendered complex visualization. | |
The data syntax is [[image, x1, y1, x2, y2], ...] where (x1, y1) and | |
(x2, y2) are where you intend the two opposite corners of the image to | |
end up in the plot. Image must be a fully loaded Javascript image (you | |
can make one with new Image()). If the image is not complete, it's | |
skipped when plotting. | |
There are two helpers included for retrieving images. The easiest work | |
the way that you put in URLs instead of images in the data (like | |
["myimage.png", 0, 0, 10, 10]), then call $.plot.image.loadData(data, | |
options, callback) where data and options are the same as you pass in | |
to $.plot. This loads the images, replaces the URLs in the data with | |
the corresponding images and calls "callback" when all images are | |
loaded (or failed loading). In the callback, you can then call $.plot | |
with the data set. See the included example. | |
A more low-level helper, $.plot.image.load(urls, callback) is also | |
included. Given a list of URLs, it calls callback with an object | |
mapping from URL to Image object when all images are loaded or have | |
failed loading. | |
Options for the plugin are | |
series: { | |
images: { | |
show: boolean | |
anchor: "corner" or "center" | |
alpha: [0,1] | |
} | |
} | |
which can be specified for a specific series | |
$.plot($("#placeholder"), [{ data: [ ... ], images: { ... } ]) | |
Note that because the data format is different from usual data points, | |
you can't use images with anything else in a specific data series. | |
Setting "anchor" to "center" causes the pixels in the image to be | |
anchored at the corner pixel centers inside of at the pixel corners, | |
effectively letting half a pixel stick out to each side in the plot. | |
A possible future direction could be support for tiling for large | |
images (like Google Maps). | |
*/ | |
(function ($) { | |
var options = { | |
series: { | |
images: { | |
show: false, | |
alpha: 1, | |
anchor: "corner" // or "center" | |
} | |
} | |
}; | |
$.plot.image = {}; | |
$.plot.image.loadDataImages = function (series, options, callback) { | |
var urls = [], points = []; | |
var defaultShow = options.series.images.show; | |
$.each(series, function (i, s) { | |
if (!(defaultShow || s.images.show)) | |
return; | |
if (s.data) | |
s = s.data; | |
$.each(s, function (i, p) { | |
if (typeof p[0] == "string") { | |
urls.push(p[0]); | |
points.push(p); | |
} | |
}); | |
}); | |
$.plot.image.load(urls, function (loadedImages) { | |
$.each(points, function (i, p) { | |
var url = p[0]; | |
if (loadedImages[url]) | |
p[0] = loadedImages[url]; | |
}); | |
callback(); | |
}); | |
} | |
$.plot.image.load = function (urls, callback) { | |
var missing = urls.length, loaded = {}; | |
if (missing == 0) | |
callback({}); | |
$.each(urls, function (i, url) { | |
var handler = function () { | |
--missing; | |
loaded[url] = this; | |
if (missing == 0) | |
callback(loaded); | |
}; | |
$('<img />').load(handler).error(handler).attr('src', url); | |
}); | |
} | |
function drawSeries(plot, ctx, series) { | |
var plotOffset = plot.getPlotOffset(); | |
if (!series.images || !series.images.show) | |
return; | |
var points = series.datapoints.points, | |
ps = series.datapoints.pointsize; | |
for (var i = 0; i < points.length; i += ps) { | |
var img = points[i], | |
x1 = points[i + 1], y1 = points[i + 2], | |
x2 = points[i + 3], y2 = points[i + 4], | |
xaxis = series.xaxis, yaxis = series.yaxis, | |
tmp; | |
// actually we should check img.complete, but it | |
// appears to be a somewhat unreliable indicator in | |
// IE6 (false even after load event) | |
if (!img || img.width <= 0 || img.height <= 0) | |
continue; | |
if (x1 > x2) { | |
tmp = x2; | |
x2 = x1; | |
x1 = tmp; | |
} | |
if (y1 > y2) { | |
tmp = y2; | |
y2 = y1; | |
y1 = tmp; | |
} | |
// if the anchor is at the center of the pixel, expand the | |
// image by 1/2 pixel in each direction | |
if (series.images.anchor == "center") { | |
tmp = 0.5 * (x2-x1) / (img.width - 1); | |
x1 -= tmp; | |
x2 += tmp; | |
tmp = 0.5 * (y2-y1) / (img.height - 1); | |
y1 -= tmp; | |
y2 += tmp; | |
} | |
// clip | |
if (x1 == x2 || y1 == y2 || | |
x1 >= xaxis.max || x2 <= xaxis.min || | |
y1 >= yaxis.max || y2 <= yaxis.min) | |
continue; | |
var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height; | |
if (x1 < xaxis.min) { | |
sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1); | |
x1 = xaxis.min; | |
} | |
if (x2 > xaxis.max) { | |
sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1); | |
x2 = xaxis.max; | |
} | |
if (y1 < yaxis.min) { | |
sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1); | |
y1 = yaxis.min; | |
} | |
if (y2 > yaxis.max) { | |
sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1); | |
y2 = yaxis.max; | |
} | |
x1 = xaxis.p2c(x1); | |
x2 = xaxis.p2c(x2); | |
y1 = yaxis.p2c(y1); | |
y2 = yaxis.p2c(y2); | |
// the transformation may have swapped us | |
if (x1 > x2) { | |
tmp = x2; | |
x2 = x1; | |
x1 = tmp; | |
} | |
if (y1 > y2) { | |
tmp = y2; | |
y2 = y1; | |
y1 = tmp; | |
} | |
tmp = ctx.globalAlpha; | |
ctx.globalAlpha *= series.images.alpha; | |
ctx.drawImage(img, | |
sx1, sy1, sx2 - sx1, sy2 - sy1, | |
x1 + plotOffset.left, y1 + plotOffset.top, | |
x2 - x1, y2 - y1); | |
ctx.globalAlpha = tmp; | |
} | |
} | |
function processRawData(plot, series, data, datapoints) { | |
if (!series.images.show) | |
return; | |
// format is Image, x1, y1, x2, y2 (opposite corners) | |
datapoints.format = [ | |
{ required: true }, | |
{ x: true, number: true, required: true }, | |
{ y: true, number: true, required: true }, | |
{ x: true, number: true, required: true }, | |
{ y: true, number: true, required: true } | |
]; | |
} | |
function init(plot) { | |
plot.hooks.processRawData.push(processRawData); | |
plot.hooks.drawSeries.push(drawSeries); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'image', | |
version: '1.1' | |
}); | |
})(jQuery); | |
(function(c){var a={series:{images:{show:false,alpha:1,anchor:"corner"}}};c.plot.image={};c.plot.image.loadDataImages=function(g,f,k){var j=[],h=[];var i=f.series.images.show;c.each(g,function(l,m){if(!(i||m.images.show)){return}if(m.data){m=m.data}c.each(m,function(n,o){if(typeof o[0]=="string"){j.push(o[0]);h.push(o)}})});c.plot.image.load(j,function(l){c.each(h,function(n,o){var m=o[0];if(l[m]){o[0]=l[m]}});k()})};c.plot.image.load=function(h,i){var g=h.length,f={};if(g==0){i({})}c.each(h,function(k,j){var l=function(){--g;f[j]=this;if(g==0){i(f)}};c("<img />").load(l).error(l).attr("src",j)})};function d(q,o,l){var m=q.getPlotOffset();if(!l.images||!l.images.show){return}var r=l.datapoints.points,n=l.datapoints.pointsize;for(var t=0;t<r.length;t+=n){var y=r[t],w=r[t+1],g=r[t+2],v=r[t+3],f=r[t+4],h=l.xaxis,u=l.yaxis,x;if(!y||y.width<=0||y.height<=0){continue}if(w>v){x=v;v=w;w=x}if(g>f){x=f;f=g;g=x}if(l.images.anchor=="center"){x=0.5*(v-w)/(y.width-1);w-=x;v+=x;x=0.5*(f-g)/(y.height-1);g-=x;f+=x}if(w==v||g==f||w>=h.max||v<=h.min||g>=u.max||f<=u.min){continue}var k=0,s=0,j=y.width,p=y.height;if(w<h.min){k+=(j-k)*(h.min-w)/(v-w);w=h.min}if(v>h.max){j+=(j-k)*(h.max-v)/(v-w);v=h.max}if(g<u.min){p+=(s-p)*(u.min-g)/(f-g);g=u.min}if(f>u.max){s+=(s-p)*(u.max-f)/(f-g);f=u.max}w=h.p2c(w);v=h.p2c(v);g=u.p2c(g);f=u.p2c(f);if(w>v){x=v;v=w;w=x}if(g>f){x=f;f=g;g=x}x=o.globalAlpha;o.globalAlpha*=l.images.alpha;o.drawImage(y,k,s,j-k,p-s,w+m.left,g+m.top,v-w,f-g);o.globalAlpha=x}}function b(i,f,g,h){if(!f.images.show){return}h.format=[{required:true},{x:true,number:true,required:true},{y:true,number:true,required:true},{x:true,number:true,required:true},{y:true,number:true,required:true}]}function e(f){f.hooks.processRawData.push(b);f.hooks.drawSeries.push(d)}c.plot.plugins.push({init:e,options:a,name:"image",version:"1.1"})})(jQuery); |
/*! Javascript plotting library for jQuery, v. 0.7. | |
* | |
* Released under the MIT license by IOLA, December 2007. | |
* | |
*/ | |
// first an inline dependency, jquery.colorhelpers.js, we inline it here | |
// for convenience | |
/* Plugin for jQuery for working with colors. | |
* | |
* Version 1.1. | |
* | |
* Inspiration from jQuery color animation plugin by John Resig. | |
* | |
* Released under the MIT license by Ole Laursen, October 2009. | |
* | |
* Examples: | |
* | |
* $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString() | |
* var c = $.color.extract($("#mydiv"), 'background-color'); | |
* console.log(c.r, c.g, c.b, c.a); | |
* $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)" | |
* | |
* Note that .scale() and .add() return the same modified object | |
* instead of making a new one. | |
* | |
* V. 1.1: Fix error handling so e.g. parsing an empty string does | |
* produce a color rather than just crashing. | |
*/ | |
(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return"rgb("+[G.r,G.g,G.b].join(",")+")"}else{return"rgba("+[G.r,G.g,G.b,G.a].join(",")+")"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=""&&E!="transparent"){break}D=D.parent()}while(!B.nodeName(D.get(0),"body"));if(E=="rgba(0, 0, 0, 0)"){E="transparent"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/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(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/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(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D=="transparent"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={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]}})(jQuery); | |
// the actual Flot code | |
(function($) { | |
function Plot(placeholder, data_, options_, plugins) { | |
// data is on the form: | |
// [ series1, series2 ... ] | |
// where series is either just the data as [ [x1, y1], [x2, y2], ... ] | |
// or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... } | |
var series = [], | |
options = { | |
// the color theme used for graphs | |
colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"], | |
legend: { | |
show: true, | |
noColumns: 1, // number of colums in legend table | |
labelFormatter: null, // fn: string -> string | |
labelBoxBorderColor: "#ccc", // border color for the little label boxes | |
container: null, // container (as jQuery object) to put legend in, null means default on top of graph | |
position: "ne", // 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 | |
}, | |
xaxis: { | |
show: null, // null = auto-detect, true = always, false = never | |
position: "bottom", // or "top" | |
mode: null, // null or "time" | |
color: null, // base color, labels, ticks | |
tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)" | |
transform: null, // null or f: number -> number to transform axis | |
inverseTransform: null, // if transform is set, this should be the inverse function | |
min: null, // min. value to show, null means set automatically | |
max: null, // max. value to show, null means set automatically | |
autoscaleMargin: null, // margin in % to add if auto-setting min/max | |
ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks | |
tickFormatter: null, // fn: number -> string | |
labelWidth: null, // size of tick labels in pixels | |
labelHeight: null, | |
reserveSpace: null, // whether to reserve space even if axis isn't shown | |
tickLength: null, // size in pixels of ticks, or "full" for whole line | |
alignTicksWithAxis: null, // axis number or null for no sync | |
// mode specific options | |
tickDecimals: null, // no. of decimals, null means auto | |
tickSize: null, // number or [number, "unit"] | |
minTickSize: null, // number or [number, "unit"] | |
monthNames: null, // list of names of months | |
timeformat: null, // format string to use | |
twelveHourClock: false // 12 or 24 time in time mode | |
}, | |
yaxis: { | |
autoscaleMargin: 0.02, | |
position: "left" // or "right" | |
}, | |
xaxes: [], | |
yaxes: [], | |
series: { | |
points: { | |
show: false, | |
radius: 3, | |
lineWidth: 2, // in pixels | |
fill: true, | |
fillColor: "#ffffff", | |
symbol: "circle" // or callback | |
}, | |
lines: { | |
// we don't put in show: false so we can see | |
// whether lines were actively disabled | |
lineWidth: 2, // in pixels | |
fill: false, | |
fillColor: null, | |
steps: false | |
}, | |
bars: { | |
show: false, | |
lineWidth: 2, // in pixels | |
barWidth: 1, // in units of the x axis | |
fill: true, | |
fillColor: null, | |
align: "left", // or "center" | |
horizontal: false | |
}, | |
shadowSize: 3 | |
}, | |
grid: { | |
show: true, | |
aboveData: false, | |
color: "#545454", // primary color used for outline and labels | |
backgroundColor: null, // null for transparent, else color | |
borderColor: null, // set if different from the grid color | |
tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)" | |
labelMargin: 5, // in pixels | |
axisMargin: 8, // in pixels | |
borderWidth: 2, // in pixels | |
minBorderMargin: null, // in pixels, null means taken from points radius | |
markings: null, // array of ranges or fn: axes -> array of ranges | |
markingsColor: "#f4f4f4", | |
markingsLineWidth: 2, | |
// interactive stuff | |
clickable: false, | |
hoverable: false, | |
autoHighlight: true, // highlight in case mouse is near | |
mouseActiveRadius: 10 // how far the mouse can be away to activate an item | |
}, | |
hooks: {} | |
}, | |
canvas = null, // the canvas for the plot itself | |
overlay = null, // canvas for interactive stuff on top of plot | |
eventHolder = null, // jQuery object that events should be bound to | |
ctx = null, octx = null, | |
xaxes = [], yaxes = [], | |
plotOffset = { left: 0, right: 0, top: 0, bottom: 0}, | |
canvasWidth = 0, canvasHeight = 0, | |
plotWidth = 0, plotHeight = 0, | |
hooks = { | |
processOptions: [], | |
processRawData: [], | |
processDatapoints: [], | |
drawSeries: [], | |
draw: [], | |
bindEvents: [], | |
drawOverlay: [], | |
shutdown: [] | |
}, | |
plot = this; | |
// public functions | |
plot.setData = setData; | |
plot.setupGrid = setupGrid; | |
plot.draw = draw; | |
plot.getPlaceholder = function() { return placeholder; }; | |
plot.getCanvas = function() { return canvas; }; | |
plot.getPlotOffset = function() { return plotOffset; }; | |
plot.width = function () { return plotWidth; }; | |
plot.height = function () { return plotHeight; }; | |
plot.offset = function () { | |
var o = eventHolder.offset(); | |
o.left += plotOffset.left; | |
o.top += plotOffset.top; | |
return o; | |
}; | |
plot.getData = function () { return series; }; | |
plot.getAxes = function () { | |
var res = {}, i; | |
$.each(xaxes.concat(yaxes), function (_, axis) { | |
if (axis) | |
res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis; | |
}); | |
return res; | |
}; | |
plot.getXAxes = function () { return xaxes; }; | |
plot.getYAxes = function () { return yaxes; }; | |
plot.c2p = canvasToAxisCoords; | |
plot.p2c = axisToCanvasCoords; | |
plot.getOptions = function () { return options; }; | |
plot.highlight = highlight; | |
plot.unhighlight = unhighlight; | |
plot.triggerRedrawOverlay = triggerRedrawOverlay; | |
plot.pointOffset = function(point) { | |
return { | |
left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left), | |
top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top) | |
}; | |
}; | |
plot.shutdown = shutdown; | |
plot.resize = function () { | |
getCanvasDimensions(); | |
resizeCanvas(canvas); | |
resizeCanvas(overlay); | |
}; | |
// public attributes | |
plot.hooks = hooks; | |
// initialize | |
initPlugins(plot); | |
parseOptions(options_); | |
setupCanvases(); | |
setData(data_); | |
setupGrid(); | |
draw(); | |
bindEvents(); | |
function executeHooks(hook, args) { | |
args = [plot].concat(args); | |
for (var i = 0; i < hook.length; ++i) | |
hook[i].apply(this, args); | |
} | |
function initPlugins() { | |
for (var i = 0; i < plugins.length; ++i) { | |
var p = plugins[i]; | |
p.init(plot); | |
if (p.options) | |
$.extend(true, options, p.options); | |
} | |
} | |
function parseOptions(opts) { | |
var i; | |
$.extend(true, options, opts); | |
if (options.xaxis.color == null) | |
options.xaxis.color = options.grid.color; | |
if (options.yaxis.color == null) | |
options.yaxis.color = options.grid.color; | |
if (options.xaxis.tickColor == null) // backwards-compatibility | |
options.xaxis.tickColor = options.grid.tickColor; | |
if (options.yaxis.tickColor == null) // backwards-compatibility | |
options.yaxis.tickColor = options.grid.tickColor; | |
if (options.grid.borderColor == null) | |
options.grid.borderColor = options.grid.color; | |
if (options.grid.tickColor == null) | |
options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString(); | |
// fill in defaults in axes, copy at least always the | |
// first as the rest of the code assumes it'll be there | |
for (i = 0; i < Math.max(1, options.xaxes.length); ++i) | |
options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]); | |
for (i = 0; i < Math.max(1, options.yaxes.length); ++i) | |
options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]); | |
// backwards compatibility, to be removed in future | |
if (options.xaxis.noTicks && options.xaxis.ticks == null) | |
options.xaxis.ticks = options.xaxis.noTicks; | |
if (options.yaxis.noTicks && options.yaxis.ticks == null) | |
options.yaxis.ticks = options.yaxis.noTicks; | |
if (options.x2axis) { | |
options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis); | |
options.xaxes[1].position = "top"; | |
} | |
if (options.y2axis) { | |
options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis); | |
options.yaxes[1].position = "right"; | |
} | |
if (options.grid.coloredAreas) | |
options.grid.markings = options.grid.coloredAreas; | |
if (options.grid.coloredAreasColor) | |
options.grid.markingsColor = options.grid.coloredAreasColor; | |
if (options.lines) | |
$.extend(true, options.series.lines, options.lines); | |
if (options.points) | |
$.extend(true, options.series.points, options.points); | |
if (options.bars) | |
$.extend(true, options.series.bars, options.bars); | |
if (options.shadowSize != null) | |
options.series.shadowSize = options.shadowSize; | |
// save options on axes for future reference | |
for (i = 0; i < options.xaxes.length; ++i) | |
getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i]; | |
for (i = 0; i < options.yaxes.length; ++i) | |
getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i]; | |
// add hooks from options | |
for (var n in hooks) | |
if (options.hooks[n] && options.hooks[n].length) | |
hooks[n] = hooks[n].concat(options.hooks[n]); | |
executeHooks(hooks.processOptions, [options]); | |
} | |
function setData(d) { | |
series = parseData(d); | |
fillInSeriesOptions(); | |
processData(); | |
} | |
function parseData(d) { | |
var res = []; | |
for (var i = 0; i < d.length; ++i) { | |
var s = $.extend(true, {}, options.series); | |
if (d[i].data != null) { | |
s.data = d[i].data; // move the data instead of deep-copy | |
delete d[i].data; | |
$.extend(true, s, d[i]); | |
d[i].data = s.data; | |
} | |
else | |
s.data = d[i]; | |
res.push(s); | |
} | |
return res; | |
} | |
function axisNumber(obj, coord) { | |
var a = obj[coord + "axis"]; | |
if (typeof a == "object") // if we got a real axis, extract number | |
a = a.n; | |
if (typeof a != "number") | |
a = 1; // default to first axis | |
return a; | |
} | |
function allAxes() { | |
// return flat array without annoying null entries | |
return $.grep(xaxes.concat(yaxes), function (a) { return a; }); | |
} | |
function canvasToAxisCoords(pos) { | |
// return an object with x/y corresponding to all used axes | |
var res = {}, i, axis; | |
for (i = 0; i < xaxes.length; ++i) { | |
axis = xaxes[i]; | |
if (axis && axis.used) | |
res["x" + axis.n] = axis.c2p(pos.left); | |
} | |
for (i = 0; i < yaxes.length; ++i) { | |
axis = yaxes[i]; | |
if (axis && axis.used) | |
res["y" + axis.n] = axis.c2p(pos.top); | |
} | |
if (res.x1 !== undefined) | |
res.x = res.x1; | |
if (res.y1 !== undefined) | |
res.y = res.y1; | |
return res; | |
} | |
function axisToCanvasCoords(pos) { | |
// get canvas coords from the first pair of x/y found in pos | |
var res = {}, i, axis, key; | |
for (i = 0; i < xaxes.length; ++i) { | |
axis = xaxes[i]; | |
if (axis && axis.used) { | |
key = "x" + axis.n; | |
if (pos[key] == null && axis.n == 1) | |
key = "x"; | |
if (pos[key] != null) { | |
res.left = axis.p2c(pos[key]); | |
break; | |
} | |
} | |
} | |
for (i = 0; i < yaxes.length; ++i) { | |
axis = yaxes[i]; | |
if (axis && axis.used) { | |
key = "y" + axis.n; | |
if (pos[key] == null && axis.n == 1) | |
key = "y"; | |
if (pos[key] != null) { | |
res.top = axis.p2c(pos[key]); | |
break; | |
} | |
} | |
} | |
return res; | |
} | |
function getOrCreateAxis(axes, number) { | |
if (!axes[number - 1]) | |
axes[number - 1] = { | |
n: number, // save the number for future reference | |
direction: axes == xaxes ? "x" : "y", | |
options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis) | |
}; | |
return axes[number - 1]; | |
} | |
function fillInSeriesOptions() { | |
var i; | |
// collect what we already got of colors | |
var neededColors = series.length, | |
usedColors = [], | |
assignedColors = []; | |
for (i = 0; i < series.length; ++i) { | |
var sc = series[i].color; | |
if (sc != null) { | |
--neededColors; | |
if (typeof sc == "number") | |
assignedColors.push(sc); | |
else | |
usedColors.push($.color.parse(series[i].color)); | |
} | |
} | |
// we might need to generate more colors if higher indices | |
// are assigned | |
for (i = 0; i < assignedColors.length; ++i) { | |
neededColors = Math.max(neededColors, assignedColors[i] + 1); | |
} | |
// produce colors as needed | |
var colors = [], variation = 0; | |
i = 0; | |
while (colors.length < neededColors) { | |
var c; | |
if (options.colors.length == i) // check degenerate case | |
c = $.color.make(100, 100, 100); | |
else | |
c = $.color.parse(options.colors[i]); | |
// vary color if needed | |
var sign = variation % 2 == 1 ? -1 : 1; | |
c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2) | |
// FIXME: if we're getting to close to something else, | |
// we should probably skip this one | |
colors.push(c); | |
++i; | |
if (i >= options.colors.length) { | |
i = 0; | |
++variation; | |
} | |
} | |
// fill in the options | |
var colori = 0, s; | |
for (i = 0; i < series.length; ++i) { | |
s = series[i]; | |
// assign colors | |
if (s.color == null) { | |
s.color = colors[colori].toString(); | |
++colori; | |
} | |
else if (typeof s.color == "number") | |
s.color = colors[s.color].toString(); | |
// turn on lines automatically in case nothing is set | |
if (s.lines.show == null) { | |
var v, show = true; | |
for (v in s) | |
if (s[v] && s[v].show) { | |
show = false; | |
break; | |
} | |
if (show) | |
s.lines.show = true; | |
} | |
// setup axes | |
s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x")); | |
s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y")); | |
} | |
} | |
function processData() { | |
var topSentry = Number.POSITIVE_INFINITY, | |
bottomSentry = Number.NEGATIVE_INFINITY, | |
fakeInfinity = Number.MAX_VALUE, | |
i, j, k, m, length, | |
s, points, ps, x, y, axis, val, f, p; | |
function updateAxis(axis, min, max) { | |
if (min < axis.datamin && min != -fakeInfinity) | |
axis.datamin = min; | |
if (max > axis.datamax && max != fakeInfinity) | |
axis.datamax = max; | |
} | |
$.each(allAxes(), function (_, axis) { | |
// init axis | |
axis.datamin = topSentry; | |
axis.datamax = bottomSentry; | |
axis.used = false; | |
}); | |
for (i = 0; i < series.length; ++i) { | |
s = series[i]; | |
s.datapoints = { points: [] }; | |
executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]); | |
} | |
// first pass: clean and copy data | |
for (i = 0; i < series.length; ++i) { | |
s = series[i]; | |
var data = s.data, format = s.datapoints.format; | |
if (!format) { | |
format = []; | |
// find out how to copy | |
format.push({ x: true, number: true, required: true }); | |
format.push({ y: true, number: true, required: true }); | |
if (s.bars.show || (s.lines.show && s.lines.fill)) { | |
format.push({ y: true, number: true, required: false, defaultValue: 0 }); | |
if (s.bars.horizontal) { | |
delete format[format.length - 1].y; | |
format[format.length - 1].x = true; | |
} | |
} | |
s.datapoints.format = format; | |
} | |
if (s.datapoints.pointsize != null) | |
continue; // already filled in | |
s.datapoints.pointsize = format.length; | |
ps = s.datapoints.pointsize; | |
points = s.datapoints.points; | |
insertSteps = s.lines.show && s.lines.steps; | |
s.xaxis.used = s.yaxis.used = true; | |
for (j = k = 0; j < data.length; ++j, k += ps) { | |
p = data[j]; | |
var nullify = p == null; | |
if (!nullify) { | |
for (m = 0; m < ps; ++m) { | |
val = p[m]; | |
f = format[m]; | |
if (f) { | |
if (f.number && val != null) { | |
val = +val; // convert to number | |
if (isNaN(val)) | |
val = null; | |
else if (val == Infinity) | |
val = fakeInfinity; | |
else if (val == -Infinity) | |
val = -fakeInfinity; | |
} | |
if (val == null) { | |
if (f.required) | |
nullify = true; | |
if (f.defaultValue != null) | |
val = f.defaultValue; | |
} | |
} | |
points[k + m] = val; | |
} | |
} | |
if (nullify) { | |
for (m = 0; m < ps; ++m) { | |
val = points[k + m]; | |
if (val != null) { | |
f = format[m]; | |
// extract min/max info | |
if (f.x) | |
updateAxis(s.xaxis, val, val); | |
if (f.y) | |
updateAxis(s.yaxis, val, val); | |
} | |
points[k + m] = null; | |
} | |
} | |
else { | |
// a little bit of line specific stuff that | |
// perhaps shouldn't be here, but lacking | |
// better means... | |
if (insertSteps && k > 0 | |
&& points[k - ps] != null | |
&& points[k - ps] != points[k] | |
&& points[k - ps + 1] != points[k + 1]) { | |
// copy the point to make room for a middle point | |
for (m = 0; m < ps; ++m) | |
points[k + ps + m] = points[k + m]; | |
// middle point has same y | |
points[k + 1] = points[k - ps + 1]; | |
// we've added a point, better reflect that | |
k += ps; | |
} | |
} | |
} | |
} | |
// give the hooks a chance to run | |
for (i = 0; i < series.length; ++i) { | |
s = series[i]; | |
executeHooks(hooks.processDatapoints, [ s, s.datapoints]); | |
} | |
// second pass: find datamax/datamin for auto-scaling | |
for (i = 0; i < series.length; ++i) { | |
s = series[i]; | |
points = s.datapoints.points, | |
ps = s.datapoints.pointsize; | |
var xmin = topSentry, ymin = topSentry, | |
xmax = bottomSentry, ymax = bottomSentry; | |
for (j = 0; j < points.length; j += ps) { | |
if (points[j] == null) | |
continue; | |
for (m = 0; m < ps; ++m) { | |
val = points[j + m]; | |
f = format[m]; | |
if (!f || val == fakeInfinity || val == -fakeInfinity) | |
continue; | |
if (f.x) { | |
if (val < xmin) | |
xmin = val; | |
if (val > xmax) | |
xmax = val; | |
} | |
if (f.y) { | |
if (val < ymin) | |
ymin = val; | |
if (val > ymax) | |
ymax = val; | |
} | |
} | |
} | |
if (s.bars.show) { | |
// make sure we got room for the bar on the dancing floor | |
var delta = s.bars.align == "left" ? 0 : -s.bars.barWidth/2; | |
if (s.bars.horizontal) { | |
ymin += delta; | |
ymax += delta + s.bars.barWidth; | |
} | |
else { | |
xmin += delta; | |
xmax += delta + s.bars.barWidth; | |
} | |
} | |
updateAxis(s.xaxis, xmin, xmax); | |
updateAxis(s.yaxis, ymin, ymax); | |
} | |
$.each(allAxes(), function (_, axis) { | |
if (axis.datamin == topSentry) | |
axis.datamin = null; | |
if (axis.datamax == bottomSentry) | |
axis.datamax = null; | |
}); | |
} | |
function makeCanvas(skipPositioning, cls) { | |
var c = document.createElement('canvas'); | |
c.className = cls; | |
c.width = canvasWidth; | |
c.height = canvasHeight; | |
if (!skipPositioning) | |
$(c).css({ position: 'absolute', left: 0, top: 0 }); | |
$(c).appendTo(placeholder); | |
if (!c.getContext) // excanvas hack | |
c = window.G_vmlCanvasManager.initElement(c); | |
// used for resetting in case we get replotted | |
c.getContext("2d").save(); | |
return c; | |
} | |
function getCanvasDimensions() { | |
canvasWidth = placeholder.width(); | |
canvasHeight = placeholder.height(); | |
if (canvasWidth <= 0 || canvasHeight <= 0) | |
throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight; | |
} | |
function resizeCanvas(c) { | |
// resizing should reset the state (excanvas seems to be | |
// buggy though) | |
if (c.width != canvasWidth) | |
c.width = canvasWidth; | |
if (c.height != canvasHeight) | |
c.height = canvasHeight; | |
// so try to get back to the initial state (even if it's | |
// gone now, this should be safe according to the spec) | |
var cctx = c.getContext("2d"); | |
cctx.restore(); | |
// and save again | |
cctx.save(); | |
} | |
function setupCanvases() { | |
var reused, | |
existingCanvas = placeholder.children("canvas.base"), | |
existingOverlay = placeholder.children("canvas.overlay"); | |
if (existingCanvas.length == 0 || existingOverlay == 0) { | |
// init everything | |
placeholder.html(""); // make sure placeholder is clear | |
placeholder.css({ padding: 0 }); // padding messes up the positioning | |
if (placeholder.css("position") == 'static') | |
placeholder.css("position", "relative"); // for positioning labels and overlay | |
getCanvasDimensions(); | |
canvas = makeCanvas(true, "base"); | |
overlay = makeCanvas(false, "overlay"); // overlay canvas for interactive features | |
reused = false; | |
} | |
else { | |
// reuse existing elements | |
canvas = existingCanvas.get(0); | |
overlay = existingOverlay.get(0); | |
reused = true; | |
} | |
ctx = canvas.getContext("2d"); | |
octx = overlay.getContext("2d"); | |
// we include the canvas in the event holder too, because IE 7 | |
// sometimes has trouble with the stacking order | |
eventHolder = $([overlay, canvas]); | |
if (reused) { | |
// run shutdown in the old plot object | |
placeholder.data("plot").shutdown(); | |
// reset reused canvases | |
plot.resize(); | |
// make sure overlay pixels are cleared (canvas is cleared when we redraw) | |
octx.clearRect(0, 0, canvasWidth, canvasHeight); | |
// then whack any remaining obvious garbage left | |
eventHolder.unbind(); | |
placeholder.children().not([canvas, overlay]).remove(); | |
} | |
// save in case we get replotted | |
placeholder.data("plot", plot); | |
} | |
function bindEvents() { | |
// bind events | |
if (options.grid.hoverable) { | |
eventHolder.mousemove(onMouseMove); | |
eventHolder.mouseleave(onMouseLeave); | |
} | |
if (options.grid.clickable) | |
eventHolder.click(onClick); | |
executeHooks(hooks.bindEvents, [eventHolder]); | |
} | |
function shutdown() { | |
if (redrawTimeout) | |
clearTimeout(redrawTimeout); | |
eventHolder.unbind("mousemove", onMouseMove); | |
eventHolder.unbind("mouseleave", onMouseLeave); | |
eventHolder.unbind("click", onClick); | |
executeHooks(hooks.shutdown, [eventHolder]); | |
} | |
function setTransformationHelpers(axis) { | |
// set helper functions on the axis, assumes plot area | |
// has been computed already | |
function identity(x) { return x; } | |
var s, m, t = axis.options.transform || identity, | |
it = axis.options.inverseTransform; | |
// precompute how much the axis is scaling a point | |
// in canvas space | |
if (axis.direction == "x") { | |
s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min)); | |
m = Math.min(t(axis.max), t(axis.min)); | |
} | |
else { | |
s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min)); | |
s = -s; | |
m = Math.max(t(axis.max), t(axis.min)); | |
} | |
// data point to canvas coordinate | |
if (t == identity) // slight optimization | |
axis.p2c = function (p) { return (p - m) * s; }; | |
else | |
axis.p2c = function (p) { return (t(p) - m) * s; }; | |
// canvas coordinate to data point | |
if (!it) | |
axis.c2p = function (c) { return m + c / s; }; | |
else | |
axis.c2p = function (c) { return it(m + c / s); }; | |
} | |
function measureTickLabels(axis) { | |
var opts = axis.options, i, ticks = axis.ticks || [], labels = [], | |
l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv; | |
function makeDummyDiv(labels, width) { | |
return $('<div style="position:absolute;top:-10000px;' + width + 'font-size:smaller">' + | |
'<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis">' | |
+ labels.join("") + '</div></div>') | |
.appendTo(placeholder); | |
} | |
if (axis.direction == "x") { | |
// to avoid measuring the widths of the labels (it's slow), we | |
// construct fixed-size boxes and put the labels inside | |
// them, we don't need the exact figures and the | |
// fixed-size box content is easy to center | |
if (w == null) | |
w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1)); | |
// measure x label heights | |
if (h == null) { | |
labels = []; | |
for (i = 0; i < ticks.length; ++i) { | |
l = ticks[i].label; | |
if (l) | |
labels.push('<div class="tickLabel" style="float:left;width:' + w + 'px">' + l + '</div>'); | |
} | |
if (labels.length > 0) { | |
// stick them all in the same div and measure | |
// collective height | |
labels.push('<div style="clear:left"></div>'); | |
dummyDiv = makeDummyDiv(labels, "width:10000px;"); | |
h = dummyDiv.height(); | |
dummyDiv.remove(); | |
} | |
} | |
} | |
else if (w == null || h == null) { | |
// calculate y label dimensions | |
for (i = 0; i < ticks.length; ++i) { | |
l = ticks[i].label; | |
if (l) | |
labels.push('<div class="tickLabel">' + l + '</div>'); | |
} | |
if (labels.length > 0) { | |
dummyDiv = makeDummyDiv(labels, ""); | |
if (w == null) | |
w = dummyDiv.children().width(); | |
if (h == null) | |
h = dummyDiv.find("div.tickLabel").height(); | |
dummyDiv.remove(); | |
} | |
} | |
if (w == null) | |
w = 0; | |
if (h == null) | |
h = 0; | |
axis.labelWidth = w; | |
axis.labelHeight = h; | |
} | |
function allocateAxisBoxFirstPhase(axis) { | |
// find the bounding box of the axis by looking at label | |
// widths/heights and ticks, make room by diminishing the | |
// plotOffset | |
var lw = axis.labelWidth, | |
lh = axis.labelHeight, | |
pos = axis.options.position, | |
tickLength = axis.options.tickLength, | |
axismargin = options.grid.axisMargin, | |
padding = options.grid.labelMargin, | |
all = axis.direction == "x" ? xaxes : yaxes, | |
index; | |
// determine axis margin | |
var samePosition = $.grep(all, function (a) { | |
return a && a.options.position == pos && a.reserveSpace; | |
}); | |
if ($.inArray(axis, samePosition) == samePosition.length - 1) | |
axismargin = 0; // outermost | |
// determine tick length - if we're innermost, we can use "full" | |
if (tickLength == null) | |
tickLength = "full"; | |
var sameDirection = $.grep(all, function (a) { | |
return a && a.reserveSpace; | |
}); | |
var innermost = $.inArray(axis, sameDirection) == 0; | |
if (!innermost && tickLength == "full") | |
tickLength = 5; | |
if (!isNaN(+tickLength)) | |
padding += +tickLength; | |
// compute box | |
if (axis.direction == "x") { | |
lh += padding; | |
if (pos == "bottom") { | |
plotOffset.bottom += lh + axismargin; | |
axis.box = { top: canvasHeight - plotOffset.bottom, height: lh }; | |
} | |
else { | |
axis.box = { top: plotOffset.top + axismargin, height: lh }; | |
plotOffset.top += lh + axismargin; | |
} | |
} | |
else { | |
lw += padding; | |
if (pos == "left") { | |
axis.box = { left: plotOffset.left + axismargin, width: lw }; | |
plotOffset.left += lw + axismargin; | |
} | |
else { | |
plotOffset.right += lw + axismargin; | |
axis.box = { left: canvasWidth - plotOffset.right, width: lw }; | |
} | |
} | |
// save for future reference | |
axis.position = pos; | |
axis.tickLength = tickLength; | |
axis.box.padding = padding; | |
axis.innermost = innermost; | |
} | |
function allocateAxisBoxSecondPhase(axis) { | |
// set remaining bounding box coordinates | |
if (axis.direction == "x") { | |
axis.box.left = plotOffset.left; | |
axis.box.width = plotWidth; | |
} | |
else { | |
axis.box.top = plotOffset.top; | |
axis.box.height = plotHeight; | |
} | |
} | |
function setupGrid() { | |
var i, axes = allAxes(); | |
// first calculate the plot and axis box dimensions | |
$.each(axes, function (_, axis) { | |
axis.show = axis.options.show; | |
if (axis.show == null) | |
axis.show = axis.used; // by default an axis is visible if it's got data | |
axis.reserveSpace = axis.show || axis.options.reserveSpace; | |
setRange(axis); | |
}); | |
allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; }); | |
plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0; | |
if (options.grid.show) { | |
$.each(allocatedAxes, function (_, axis) { | |
// make the ticks | |
setupTickGeneration(axis); | |
setTicks(axis); | |
snapRangeToTicks(axis, axis.ticks); | |
// find labelWidth/Height for axis | |
measureTickLabels(axis); | |
}); | |
// with all dimensions in house, we can compute the | |
// axis boxes, start from the outside (reverse order) | |
for (i = allocatedAxes.length - 1; i >= 0; --i) | |
allocateAxisBoxFirstPhase(allocatedAxes[i]); | |
// make sure we've got enough space for things that | |
// might stick out | |
var minMargin = options.grid.minBorderMargin; | |
if (minMargin == null) { | |
minMargin = 0; | |
for (i = 0; i < series.length; ++i) | |
minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2); | |
} | |
for (var a in plotOffset) { | |
plotOffset[a] += options.grid.borderWidth; | |
plotOffset[a] = Math.max(minMargin, plotOffset[a]); | |
} | |
} | |
plotWidth = canvasWidth - plotOffset.left - plotOffset.right; | |
plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top; | |
// now we got the proper plotWidth/Height, we can compute the scaling | |
$.each(axes, function (_, axis) { | |
setTransformationHelpers(axis); | |
}); | |
if (options.grid.show) { | |
$.each(allocatedAxes, function (_, axis) { | |
allocateAxisBoxSecondPhase(axis); | |
}); | |
insertAxisLabels(); | |
} | |
insertLegend(); | |
} | |
function setRange(axis) { | |
var opts = axis.options, | |
min = +(opts.min != null ? opts.min : axis.datamin), | |
max = +(opts.max != null ? opts.max : axis.datamax), | |
delta = max - min; | |
if (delta == 0.0) { | |
// degenerate case | |
var widen = max == 0 ? 1 : 0.01; | |
if (opts.min == null) | |
min -= widen; | |
// always widen max if we couldn't widen min to ensure we | |
// don't fall into min == max which doesn't work | |
if (opts.max == null || opts.min != null) | |
max += widen; | |
} | |
else { | |
// consider autoscaling | |
var margin = opts.autoscaleMargin; | |
if (margin != null) { | |
if (opts.min == null) { | |
min -= delta * margin; | |
// make sure we don't go below zero if all values | |
// are positive | |
if (min < 0 && axis.datamin != null && axis.datamin >= 0) | |
min = 0; | |
} | |
if (opts.max == null) { | |
max += delta * margin; | |
if (max > 0 && axis.datamax != null && axis.datamax <= 0) | |
max = 0; | |
} | |
} | |
} | |
axis.min = min; | |
axis.max = max; | |
} | |
function setupTickGeneration(axis) { | |
var opts = axis.options; | |
// estimate number of ticks | |
var noTicks; | |
if (typeof opts.ticks == "number" && opts.ticks > 0) | |
noTicks = opts.ticks; | |
else | |
// heuristic based on the model a*sqrt(x) fitted to | |
// some data points that seemed reasonable | |
noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? canvasWidth : canvasHeight); | |
var delta = (axis.max - axis.min) / noTicks, | |
size, generator, unit, formatter, i, magn, norm; | |
if (opts.mode == "time") { | |
// pretty handling of time | |
// map of app. size of time units in milliseconds | |
var timeUnitSize = { | |
"second": 1000, | |
"minute": 60 * 1000, | |
"hour": 60 * 60 * 1000, | |
"day": 24 * 60 * 60 * 1000, | |
"month": 30 * 24 * 60 * 60 * 1000, | |
"year": 365.2425 * 24 * 60 * 60 * 1000 | |
}; | |
// the allowed tick sizes, after 1 year we use | |
// an integer algorithm | |
var spec = [ | |
[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"] | |
]; | |
var minSize = 0; | |
if (opts.minTickSize != null) { | |
if (typeof opts.tickSize == "number") | |
minSize = opts.tickSize; | |
else | |
minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]]; | |
} | |
for (var i = 0; i < spec.length - 1; ++i) | |
if (delta < (spec[i][0] * timeUnitSize[spec[i][1]] | |
+ spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2 | |
&& spec[i][0] * timeUnitSize[spec[i][1]] >= minSize) | |
break; | |
size = spec[i][0]; | |
unit = spec[i][1]; | |
// special-case the possibility of several years | |
if (unit == "year") { | |
magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10)); | |
norm = (delta / timeUnitSize.year) / magn; | |
if (norm < 1.5) | |
size = 1; | |
else if (norm < 3) | |
size = 2; | |
else if (norm < 7.5) | |
size = 5; | |
else | |
size = 10; | |
size *= magn; | |
} | |
axis.tickSize = opts.tickSize || [size, unit]; | |
generator = function(axis) { | |
var ticks = [], | |
tickSize = axis.tickSize[0], unit = axis.tickSize[1], | |
d = new Date(axis.min); | |
var step = tickSize * timeUnitSize[unit]; | |
if (unit == "second") | |
d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize)); | |
if (unit == "minute") | |
d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize)); | |
if (unit == "hour") | |
d.setUTCHours(floorInBase(d.getUTCHours(), tickSize)); | |
if (unit == "month") | |
d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize)); | |
if (unit == "year") | |
d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize)); | |
// reset smaller components | |
d.setUTCMilliseconds(0); | |
if (step >= timeUnitSize.minute) | |
d.setUTCSeconds(0); | |
if (step >= timeUnitSize.hour) | |
d.setUTCMinutes(0); | |
if (step >= timeUnitSize.day) | |
d.setUTCHours(0); | |
if (step >= timeUnitSize.day * 4) | |
d.setUTCDate(1); | |
if (step >= timeUnitSize.year) | |
d.setUTCMonth(0); | |
var carry = 0, v = Number.NaN, prev; | |
do { | |
prev = v; | |
v = d.getTime(); | |
ticks.push(v); | |
if (unit == "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 | |
d.setUTCDate(1); | |
var start = d.getTime(); | |
d.setUTCMonth(d.getUTCMonth() + 1); | |
var end = d.getTime(); | |
d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize); | |
carry = d.getUTCHours(); | |
d.setUTCHours(0); | |
} | |
else | |
d.setUTCMonth(d.getUTCMonth() + tickSize); | |
} | |
else if (unit == "year") { | |
d.setUTCFullYear(d.getUTCFullYear() + tickSize); | |
} | |
else | |
d.setTime(v + step); | |
} while (v < axis.max && v != prev); | |
return ticks; | |
}; | |
formatter = function (v, axis) { | |
var d = new Date(v); | |
// first check global format | |
if (opts.timeformat != null) | |
return $.plot.formatDate(d, opts.timeformat, opts.monthNames); | |
var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]]; | |
var span = axis.max - axis.min; | |
var suffix = (opts.twelveHourClock) ? " %p" : ""; | |
if (t < timeUnitSize.minute) | |
fmt = "%h:%M:%S" + suffix; | |
else if (t < timeUnitSize.day) { | |
if (span < 2 * timeUnitSize.day) | |
fmt = "%h:%M" + suffix; | |
else | |
fmt = "%b %d %h:%M" + suffix; | |
} | |
else if (t < timeUnitSize.month) | |
fmt = "%b %d"; | |
else if (t < timeUnitSize.year) { | |
if (span < timeUnitSize.year) | |
fmt = "%b"; | |
else | |
fmt = "%b %y"; | |
} | |
else | |
fmt = "%y"; | |
return $.plot.formatDate(d, fmt, opts.monthNames); | |
}; | |
} | |
else { | |
// pretty rounding of base-10 numbers | |
var maxDec = opts.tickDecimals; | |
var dec = -Math.floor(Math.log(delta) / Math.LN10); | |
if (maxDec != null && dec > maxDec) | |
dec = maxDec; | |
magn = Math.pow(10, -dec); | |
norm = delta / magn; // norm is between 1.0 and 10.0 | |
if (norm < 1.5) | |
size = 1; | |
else if (norm < 3) { | |
size = 2; | |
// special case for 2.5, requires an extra decimal | |
if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) { | |
size = 2.5; | |
++dec; | |
} | |
} | |
else if (norm < 7.5) | |
size = 5; | |
else | |
size = 10; | |
size *= magn; | |
if (opts.minTickSize != null && size < opts.minTickSize) | |
size = opts.minTickSize; | |
axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec); | |
axis.tickSize = opts.tickSize || size; | |
generator = function (axis) { | |
var ticks = []; | |
// spew out all possible ticks | |
var start = floorInBase(axis.min, axis.tickSize), | |
i = 0, v = Number.NaN, prev; | |
do { | |
prev = v; | |
v = start + i * axis.tickSize; | |
ticks.push(v); | |
++i; | |
} while (v < axis.max && v != prev); | |
return ticks; | |
}; | |
formatter = function (v, axis) { | |
return v.toFixed(axis.tickDecimals); | |
}; | |
} | |
if (opts.alignTicksWithAxis != null) { | |
var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1]; | |
if (otherAxis && otherAxis.used && otherAxis != axis) { | |
// consider snapping min/max to outermost nice ticks | |
var niceTicks = generator(axis); | |
if (niceTicks.length > 0) { | |
if (opts.min == null) | |
axis.min = Math.min(axis.min, niceTicks[0]); | |
if (opts.max == null && niceTicks.length > 1) | |
axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]); | |
} | |
generator = function (axis) { | |
// copy ticks, scaled to this axis | |
var ticks = [], v, i; | |
for (i = 0; i < otherAxis.ticks.length; ++i) { | |
v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min); | |
v = axis.min + v * (axis.max - axis.min); | |
ticks.push(v); | |
} | |
return ticks; | |
}; | |
// we might need an extra decimal since forced | |
// ticks don't necessarily fit naturally | |
if (axis.mode != "time" && opts.tickDecimals == null) { | |
var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1), | |
ts = generator(axis); | |
// only proceed if the tick interval rounded | |
// with an extra decimal doesn't give us a | |
// zero at end | |
if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec)))) | |
axis.tickDecimals = extraDec; | |
} | |
} | |
} | |
axis.tickGenerator = generator; | |
if ($.isFunction(opts.tickFormatter)) | |
axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); }; | |
else | |
axis.tickFormatter = formatter; | |
} | |
function setTicks(axis) { | |
var oticks = axis.options.ticks, ticks = []; | |
if (oticks == null || (typeof oticks == "number" && oticks > 0)) | |
ticks = axis.tickGenerator(axis); | |
else if (oticks) { | |
if ($.isFunction(oticks)) | |
// generate the ticks | |
ticks = oticks({ min: axis.min, max: axis.max }); | |
else | |
ticks = oticks; | |
} | |
// clean up/labelify the supplied ticks, copy them over | |
var i, v; | |
axis.ticks = []; | |
for (i = 0; i < ticks.length; ++i) { | |
var label = null; | |
var t = ticks[i]; | |
if (typeof t == "object") { | |
v = +t[0]; | |
if (t.length > 1) | |
label = t[1]; | |
} | |
else | |
v = +t; | |
if (label == null) | |
label = axis.tickFormatter(v, axis); | |
if (!isNaN(v)) | |
axis.ticks.push({ v: v, label: label }); | |
} | |
} | |
function snapRangeToTicks(axis, ticks) { | |
if (axis.options.autoscaleMargin && ticks.length > 0) { | |
// snap to ticks | |
if (axis.options.min == null) | |
axis.min = Math.min(axis.min, ticks[0].v); | |
if (axis.options.max == null && ticks.length > 1) | |
axis.max = Math.max(axis.max, ticks[ticks.length - 1].v); | |
} | |
} | |
function draw() { | |
ctx.clearRect(0, 0, canvasWidth, canvasHeight); | |
var grid = options.grid; | |
// draw background, if any | |
if (grid.show && grid.backgroundColor) | |
drawBackground(); | |
if (grid.show && !grid.aboveData) | |
drawGrid(); | |
for (var i = 0; i < series.length; ++i) { | |
executeHooks(hooks.drawSeries, [ctx, series[i]]); | |
drawSeries(series[i]); | |
} | |
executeHooks(hooks.draw, [ctx]); | |
if (grid.show && grid.aboveData) | |
drawGrid(); | |
} | |
function extractRange(ranges, coord) { | |
var axis, from, to, key, axes = allAxes(); | |
for (i = 0; i < axes.length; ++i) { | |
axis = axes[i]; | |
if (axis.direction == coord) { | |
key = coord + axis.n + "axis"; | |
if (!ranges[key] && axis.n == 1) | |
key = coord + "axis"; // support x1axis as xaxis | |
if (ranges[key]) { | |
from = ranges[key].from; | |
to = ranges[key].to; | |
break; | |
} | |
} | |
} | |
// backwards-compat stuff - to be removed in future | |
if (!ranges[key]) { | |
axis = coord == "x" ? xaxes[0] : yaxes[0]; | |
from = ranges[coord + "1"]; | |
to = ranges[coord + "2"]; | |
} | |
// auto-reverse as an added bonus | |
if (from != null && to != null && from > to) { | |
var tmp = from; | |
from = to; | |
to = tmp; | |
} | |
return { from: from, to: to, axis: axis }; | |
} | |
function drawBackground() { | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)"); | |
ctx.fillRect(0, 0, plotWidth, plotHeight); | |
ctx.restore(); | |
} | |
function drawGrid() { | |
var i; | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
// draw markings | |
var markings = options.grid.markings; | |
if (markings) { | |
if ($.isFunction(markings)) { | |
var axes = plot.getAxes(); | |
// xmin etc. is backwards compatibility, to be | |
// removed in the future | |
axes.xmin = axes.xaxis.min; | |
axes.xmax = axes.xaxis.max; | |
axes.ymin = axes.yaxis.min; | |
axes.ymax = axes.yaxis.max; | |
markings = markings(axes); | |
} | |
for (i = 0; i < markings.length; ++i) { | |
var m = markings[i], | |
xrange = extractRange(m, "x"), | |
yrange = extractRange(m, "y"); | |
// fill in missing | |
if (xrange.from == null) | |
xrange.from = xrange.axis.min; | |
if (xrange.to == null) | |
xrange.to = xrange.axis.max; | |
if (yrange.from == null) | |
yrange.from = yrange.axis.min; | |
if (yrange.to == null) | |
yrange.to = yrange.axis.max; | |
// clip | |
if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max || | |
yrange.to < yrange.axis.min || yrange.from > yrange.axis.max) | |
continue; | |
xrange.from = Math.max(xrange.from, xrange.axis.min); | |
xrange.to = Math.min(xrange.to, xrange.axis.max); | |
yrange.from = Math.max(yrange.from, yrange.axis.min); | |
yrange.to = Math.min(yrange.to, yrange.axis.max); | |
if (xrange.from == xrange.to && yrange.from == yrange.to) | |
continue; | |
// then draw | |
xrange.from = xrange.axis.p2c(xrange.from); | |
xrange.to = xrange.axis.p2c(xrange.to); | |
yrange.from = yrange.axis.p2c(yrange.from); | |
yrange.to = yrange.axis.p2c(yrange.to); | |
if (xrange.from == xrange.to || yrange.from == yrange.to) { | |
// draw line | |
ctx.beginPath(); | |
ctx.strokeStyle = m.color || options.grid.markingsColor; | |
ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth; | |
ctx.moveTo(xrange.from, yrange.from); | |
ctx.lineTo(xrange.to, yrange.to); | |
ctx.stroke(); | |
} | |
else { | |
// fill area | |
ctx.fillStyle = m.color || options.grid.markingsColor; | |
ctx.fillRect(xrange.from, yrange.to, | |
xrange.to - xrange.from, | |
yrange.from - yrange.to); | |
} | |
} | |
} | |
// draw the ticks | |
var axes = allAxes(), bw = options.grid.borderWidth; | |
for (var j = 0; j < axes.length; ++j) { | |
var axis = axes[j], box = axis.box, | |
t = axis.tickLength, x, y, xoff, yoff; | |
if (!axis.show || axis.ticks.length == 0) | |
continue | |
ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString(); | |
ctx.lineWidth = 1; | |
// find the edges | |
if (axis.direction == "x") { | |
x = 0; | |
if (t == "full") | |
y = (axis.position == "top" ? 0 : plotHeight); | |
else | |
y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0); | |
} | |
else { | |
y = 0; | |
if (t == "full") | |
x = (axis.position == "left" ? 0 : plotWidth); | |
else | |
x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0); | |
} | |
// draw tick bar | |
if (!axis.innermost) { | |
ctx.beginPath(); | |
xoff = yoff = 0; | |
if (axis.direction == "x") | |
xoff = plotWidth; | |
else | |
yoff = plotHeight; | |
if (ctx.lineWidth == 1) { | |
x = Math.floor(x) + 0.5; | |
y = Math.floor(y) + 0.5; | |
} | |
ctx.moveTo(x, y); | |
ctx.lineTo(x + xoff, y + yoff); | |
ctx.stroke(); | |
} | |
// draw ticks | |
ctx.beginPath(); | |
for (i = 0; i < axis.ticks.length; ++i) { | |
var v = axis.ticks[i].v; | |
xoff = yoff = 0; | |
if (v < axis.min || v > axis.max | |
// skip those lying on the axes if we got a border | |
|| (t == "full" && bw > 0 | |
&& (v == axis.min || v == axis.max))) | |
continue; | |
if (axis.direction == "x") { | |
x = axis.p2c(v); | |
yoff = t == "full" ? -plotHeight : t; | |
if (axis.position == "top") | |
yoff = -yoff; | |
} | |
else { | |
y = axis.p2c(v); | |
xoff = t == "full" ? -plotWidth : t; | |
if (axis.position == "left") | |
xoff = -xoff; | |
} | |
if (ctx.lineWidth == 1) { | |
if (axis.direction == "x") | |
x = Math.floor(x) + 0.5; | |
else | |
y = Math.floor(y) + 0.5; | |
} | |
ctx.moveTo(x, y); | |
ctx.lineTo(x + xoff, y + yoff); | |
} | |
ctx.stroke(); | |
} | |
// draw border | |
if (bw) { | |
ctx.lineWidth = bw; | |
ctx.strokeStyle = options.grid.borderColor; | |
ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw); | |
} | |
ctx.restore(); | |
} | |
function insertAxisLabels() { | |
placeholder.find(".tickLabels").remove(); | |
var html = ['<div class="tickLabels" style="font-size:smaller">']; | |
var axes = allAxes(); | |
for (var j = 0; j < axes.length; ++j) { | |
var axis = axes[j], box = axis.box; | |
if (!axis.show) | |
continue; | |
//debug: html.push('<div style="position:absolute;opacity:0.10;background-color:red;left:' + box.left + 'px;top:' + box.top + 'px;width:' + box.width + 'px;height:' + box.height + 'px"></div>') | |
html.push('<div class="' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis" style="color:' + axis.options.color + '">'); | |
for (var i = 0; i < axis.ticks.length; ++i) { | |
var tick = axis.ticks[i]; | |
if (!tick.label || tick.v < axis.min || tick.v > axis.max) | |
continue; | |
var pos = {}, align; | |
if (axis.direction == "x") { | |
align = "center"; | |
pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2); | |
if (axis.position == "bottom") | |
pos.top = box.top + box.padding; | |
else | |
pos.bottom = canvasHeight - (box.top + box.height - box.padding); | |
} | |
else { | |
pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2); | |
if (axis.position == "left") { | |
pos.right = canvasWidth - (box.left + box.width - box.padding) | |
align = "right"; | |
} | |
else { | |
pos.left = box.left + box.padding; | |
align = "left"; | |
} | |
} | |
pos.width = axis.labelWidth; | |
var style = ["position:absolute", "text-align:" + align ]; | |
for (var a in pos) | |
style.push(a + ":" + pos[a] + "px") | |
html.push('<div class="tickLabel" style="' + style.join(';') + '">' + tick.label + '</div>'); | |
} | |
html.push('</div>'); | |
} | |
html.push('</div>'); | |
placeholder.append(html.join("")); | |
} | |
function drawSeries(series) { | |
if (series.lines.show) | |
drawSeriesLines(series); | |
if (series.bars.show) | |
drawSeriesBars(series); | |
if (series.points.show) | |
drawSeriesPoints(series); | |
} | |
function drawSeriesLines(series) { | |
function plotLine(datapoints, xoffset, yoffset, axisx, axisy) { | |
var points = datapoints.points, | |
ps = datapoints.pointsize, | |
prevx = null, prevy = null; | |
ctx.beginPath(); | |
for (var i = ps; i < points.length; i += ps) { | |
var x1 = points[i - ps], y1 = points[i - ps + 1], | |
x2 = points[i], y2 = points[i + 1]; | |
if (x1 == null || x2 == null) | |
continue; | |
// clip with ymin | |
if (y1 <= y2 && y1 < axisy.min) { | |
if (y2 < axisy.min) | |
continue; // line segment is outside | |
// compute new intersection point | |
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = axisy.min; | |
} | |
else if (y2 <= y1 && y2 < axisy.min) { | |
if (y1 < axisy.min) | |
continue; | |
x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = axisy.min; | |
} | |
// clip with ymax | |
if (y1 >= y2 && y1 > axisy.max) { | |
if (y2 > axisy.max) | |
continue; | |
x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = axisy.max; | |
} | |
else if (y2 >= y1 && y2 > axisy.max) { | |
if (y1 > axisy.max) | |
continue; | |
x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = axisy.max; | |
} | |
// clip with xmin | |
if (x1 <= x2 && x1 < axisx.min) { | |
if (x2 < axisx.min) | |
continue; | |
y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = axisx.min; | |
} | |
else if (x2 <= x1 && x2 < axisx.min) { | |
if (x1 < axisx.min) | |
continue; | |
y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = axisx.min; | |
} | |
// clip with xmax | |
if (x1 >= x2 && x1 > axisx.max) { | |
if (x2 > axisx.max) | |
continue; | |
y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = axisx.max; | |
} | |
else if (x2 >= x1 && x2 > axisx.max) { | |
if (x1 > axisx.max) | |
continue; | |
y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = axisx.max; | |
} | |
if (x1 != prevx || y1 != prevy) | |
ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset); | |
prevx = x2; | |
prevy = y2; | |
ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset); | |
} | |
ctx.stroke(); | |
} | |
function plotLineArea(datapoints, axisx, axisy) { | |
var points = datapoints.points, | |
ps = datapoints.pointsize, | |
bottom = Math.min(Math.max(0, axisy.min), axisy.max), | |
i = 0, top, areaOpen = false, | |
ypos = 1, segmentStart = 0, segmentEnd = 0; | |
// we process each segment in two turns, first forward | |
// direction to sketch out top, then once we hit the | |
// end we go backwards to sketch the bottom | |
while (true) { | |
if (ps > 0 && i > points.length + ps) | |
break; | |
i += ps; // ps is negative if going backwards | |
var x1 = points[i - ps], | |
y1 = points[i - ps + ypos], | |
x2 = points[i], y2 = points[i + ypos]; | |
if (areaOpen) { | |
if (ps > 0 && x1 != null && x2 == null) { | |
// at turning point | |
segmentEnd = i; | |
ps = -ps; | |
ypos = 2; | |
continue; | |
} | |
if (ps < 0 && i == segmentStart + ps) { | |
// done with the reverse sweep | |
ctx.fill(); | |
areaOpen = false; | |
ps = -ps; | |
ypos = 1; | |
i = segmentStart = segmentEnd + ps; | |
continue; | |
} | |
} | |
if (x1 == null || x2 == null) | |
continue; | |
// clip x values | |
// clip with xmin | |
if (x1 <= x2 && x1 < axisx.min) { | |
if (x2 < axisx.min) | |
continue; | |
y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = axisx.min; | |
} | |
else if (x2 <= x1 && x2 < axisx.min) { | |
if (x1 < axisx.min) | |
continue; | |
y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = axisx.min; | |
} | |
// clip with xmax | |
if (x1 >= x2 && x1 > axisx.max) { | |
if (x2 > axisx.max) | |
continue; | |
y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = axisx.max; | |
} | |
else if (x2 >= x1 && x2 > axisx.max) { | |
if (x1 > axisx.max) | |
continue; | |
y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = axisx.max; | |
} | |
if (!areaOpen) { | |
// open area | |
ctx.beginPath(); | |
ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom)); | |
areaOpen = true; | |
} | |
// now first check the case where both is outside | |
if (y1 >= axisy.max && y2 >= axisy.max) { | |
ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max)); | |
ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max)); | |
continue; | |
} | |
else if (y1 <= axisy.min && y2 <= axisy.min) { | |
ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min)); | |
ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min)); | |
continue; | |
} | |
// else it's a bit more complicated, there might | |
// be a flat maxed out rectangle first, then a | |
// triangular cutout or reverse; to find these | |
// keep track of the current x values | |
var x1old = x1, x2old = x2; | |
// clip the y values, without shortcutting, we | |
// go through all cases in turn | |
// clip with ymin | |
if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) { | |
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = axisy.min; | |
} | |
else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) { | |
x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = axisy.min; | |
} | |
// clip with ymax | |
if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) { | |
x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = axisy.max; | |
} | |
else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) { | |
x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = axisy.max; | |
} | |
// if the x value was changed we got a rectangle | |
// to fill | |
if (x1 != x1old) { | |
ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1)); | |
// it goes to (x1, y1), but we fill that below | |
} | |
// fill triangular section, this sometimes result | |
// in redundant points if (x1, y1) hasn't changed | |
// from previous line to, but we just ignore that | |
ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1)); | |
ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); | |
// fill the other rectangle if it's there | |
if (x2 != x2old) { | |
ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2)); | |
ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2)); | |
} | |
} | |
} | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
ctx.lineJoin = "round"; | |
var lw = series.lines.lineWidth, | |
sw = series.shadowSize; | |
// FIXME: consider another form of shadow when filling is turned on | |
if (lw > 0 && sw > 0) { | |
// draw shadow as a thick and thin line with transparency | |
ctx.lineWidth = sw; | |
ctx.strokeStyle = "rgba(0,0,0,0.1)"; | |
// position shadow at angle from the mid of line | |
var angle = Math.PI/18; | |
plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis); | |
ctx.lineWidth = sw/2; | |
plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis); | |
} | |
ctx.lineWidth = lw; | |
ctx.strokeStyle = series.color; | |
var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight); | |
if (fillStyle) { | |
ctx.fillStyle = fillStyle; | |
plotLineArea(series.datapoints, series.xaxis, series.yaxis); | |
} | |
if (lw > 0) | |
plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis); | |
ctx.restore(); | |
} | |
function drawSeriesPoints(series) { | |
function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) { | |
var points = datapoints.points, ps = datapoints.pointsize; | |
for (var i = 0; i < points.length; i += ps) { | |
var x = points[i], y = points[i + 1]; | |
if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) | |
continue; | |
ctx.beginPath(); | |
x = axisx.p2c(x); | |
y = axisy.p2c(y) + offset; | |
if (symbol == "circle") | |
ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false); | |
else | |
symbol(ctx, x, y, radius, shadow); | |
ctx.closePath(); | |
if (fillStyle) { | |
ctx.fillStyle = fillStyle; | |
ctx.fill(); | |
} | |
ctx.stroke(); | |
} | |
} | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
var lw = series.points.lineWidth, | |
sw = series.shadowSize, | |
radius = series.points.radius, | |
symbol = series.points.symbol; | |
if (lw > 0 && sw > 0) { | |
// draw shadow in two steps | |
var w = sw / 2; | |
ctx.lineWidth = w; | |
ctx.strokeStyle = "rgba(0,0,0,0.1)"; | |
plotPoints(series.datapoints, radius, null, w + w/2, true, | |
series.xaxis, series.yaxis, symbol); | |
ctx.strokeStyle = "rgba(0,0,0,0.2)"; | |
plotPoints(series.datapoints, radius, null, w/2, true, | |
series.xaxis, series.yaxis, symbol); | |
} | |
ctx.lineWidth = lw; | |
ctx.strokeStyle = series.color; | |
plotPoints(series.datapoints, radius, | |
getFillStyle(series.points, series.color), 0, false, | |
series.xaxis, series.yaxis, symbol); | |
ctx.restore(); | |
} | |
function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) { | |
var left, right, bottom, top, | |
drawLeft, drawRight, drawTop, drawBottom, | |
tmp; | |
// in horizontal mode, we start the bar from the left | |
// instead of from the bottom so it appears to be | |
// horizontal rather than vertical | |
if (horizontal) { | |
drawBottom = drawRight = drawTop = true; | |
drawLeft = false; | |
left = b; | |
right = x; | |
top = y + barLeft; | |
bottom = y + barRight; | |
// account for negative bars | |
if (right < left) { | |
tmp = right; | |
right = left; | |
left = tmp; | |
drawLeft = true; | |
drawRight = false; | |
} | |
} | |
else { | |
drawLeft = drawRight = drawTop = true; | |
drawBottom = false; | |
left = x + barLeft; | |
right = x + barRight; | |
bottom = b; | |
top = y; | |
// account for negative bars | |
if (top < bottom) { | |
tmp = top; | |
top = bottom; | |
bottom = tmp; | |
drawBottom = true; | |
drawTop = false; | |
} | |
} | |
// clip | |
if (right < axisx.min || left > axisx.max || | |
top < axisy.min || bottom > axisy.max) | |
return; | |
if (left < axisx.min) { | |
left = axisx.min; | |
drawLeft = false; | |
} | |
if (right > axisx.max) { | |
right = axisx.max; | |
drawRight = false; | |
} | |
if (bottom < axisy.min) { | |
bottom = axisy.min; | |
drawBottom = false; | |
} | |
if (top > axisy.max) { | |
top = axisy.max; | |
drawTop = false; | |
} | |
left = axisx.p2c(left); | |
bottom = axisy.p2c(bottom); | |
right = axisx.p2c(right); | |
top = axisy.p2c(top); | |
// fill the bar | |
if (fillStyleCallback) { | |
c.beginPath(); | |
c.moveTo(left, bottom); | |
c.lineTo(left, top); | |
c.lineTo(right, top); | |
c.lineTo(right, bottom); | |
c.fillStyle = fillStyleCallback(bottom, top); | |
c.fill(); | |
} | |
// draw outline | |
if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) { | |
c.beginPath(); | |
// FIXME: inline moveTo is buggy with excanvas | |
c.moveTo(left, bottom + offset); | |
if (drawLeft) | |
c.lineTo(left, top + offset); | |
else | |
c.moveTo(left, top + offset); | |
if (drawTop) | |
c.lineTo(right, top + offset); | |
else | |
c.moveTo(right, top + offset); | |
if (drawRight) | |
c.lineTo(right, bottom + offset); | |
else | |
c.moveTo(right, bottom + offset); | |
if (drawBottom) | |
c.lineTo(left, bottom + offset); | |
else | |
c.moveTo(left, bottom + offset); | |
c.stroke(); | |
} | |
} | |
function drawSeriesBars(series) { | |
function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) { | |
var points = datapoints.points, ps = datapoints.pointsize; | |
for (var i = 0; i < points.length; i += ps) { | |
if (points[i] == null) | |
continue; | |
drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth); | |
} | |
} | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
// FIXME: figure out a way to add shadows (for instance along the right edge) | |
ctx.lineWidth = series.bars.lineWidth; | |
ctx.strokeStyle = series.color; | |
var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2; | |
var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null; | |
plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis); | |
ctx.restore(); | |
} | |
function getFillStyle(filloptions, seriesColor, bottom, top) { | |
var fill = filloptions.fill; | |
if (!fill) | |
return null; | |
if (filloptions.fillColor) | |
return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor); | |
var c = $.color.parse(seriesColor); | |
c.a = typeof fill == "number" ? fill : 0.4; | |
c.normalize(); | |
return c.toString(); | |
} | |
function insertLegend() { | |
placeholder.find(".legend").remove(); | |
if (!options.legend.show) | |
return; | |
var fragments = [], rowStarted = false, | |
lf = options.legend.labelFormatter, s, label; | |
for (var i = 0; i < series.length; ++i) { | |
s = series[i]; | |
label = s.label; | |
if (!label) | |
continue; | |
if (i % options.legend.noColumns == 0) { | |
if (rowStarted) | |
fragments.push('</tr>'); | |
fragments.push('<tr>'); | |
rowStarted = true; | |
} | |
if (lf) | |
label = lf(label, s); | |
fragments.push( | |
'<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden"></div></div></td>' + | |
'<td class="legendLabel">' + label + '</td>'); | |
} | |
if (rowStarted) | |
fragments.push('</tr>'); | |
if (fragments.length == 0) | |
return; | |
var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>'; | |
if (options.legend.container != null) | |
$(options.legend.container).html(table); | |
else { | |
var pos = "", | |
p = options.legend.position, | |
m = options.legend.margin; | |
if (m[0] == null) | |
m = [m, m]; | |
if (p.charAt(0) == "n") | |
pos += 'top:' + (m[1] + plotOffset.top) + 'px;'; | |
else if (p.charAt(0) == "s") | |
pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;'; | |
if (p.charAt(1) == "e") | |
pos += 'right:' + (m[0] + plotOffset.right) + 'px;'; | |
else if (p.charAt(1) == "w") | |
pos += 'left:' + (m[0] + plotOffset.left) + 'px;'; | |
var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder); | |
if (options.legend.backgroundOpacity != 0.0) { | |
// put in the transparent background | |
// separately to avoid blended labels and | |
// label boxes | |
var c = options.legend.backgroundColor; | |
if (c == null) { | |
c = options.grid.backgroundColor; | |
if (c && typeof c == "string") | |
c = $.color.parse(c); | |
else | |
c = $.color.extract(legend, 'background-color'); | |
c.a = 1; | |
c = c.toString(); | |
} | |
var div = legend.children(); | |
$('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity); | |
} | |
} | |
} | |
// interactive features | |
var highlights = [], | |
redrawTimeout = null; | |
// returns the data item the mouse is over, or null if none is found | |
function findNearbyItem(mouseX, mouseY, seriesFilter) { | |
var maxDistance = options.grid.mouseActiveRadius, | |
smallestDistance = maxDistance * maxDistance + 1, | |
item = null, foundPoint = false, i, j; | |
for (i = series.length - 1; i >= 0; --i) { | |
if (!seriesFilter(series[i])) | |
continue; | |
var s = series[i], | |
axisx = s.xaxis, | |
axisy = s.yaxis, | |
points = s.datapoints.points, | |
ps = s.datapoints.pointsize, | |
mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster | |
my = axisy.c2p(mouseY), | |
maxx = maxDistance / axisx.scale, | |
maxy = maxDistance / axisy.scale; | |
// with inverse transforms, we can't use the maxx/maxy | |
// optimization, sadly | |
if (axisx.options.inverseTransform) | |
maxx = Number.MAX_VALUE; | |
if (axisy.options.inverseTransform) | |
maxy = Number.MAX_VALUE; | |
if (s.lines.show || s.points.show) { | |
for (j = 0; j < points.length; j += ps) { | |
var x = points[j], y = points[j + 1]; | |
if (x == null) | |
continue; | |
// For points and lines, the cursor must be within a | |
// certain distance to the data point | |
if (x - mx > maxx || x - mx < -maxx || | |
y - my > maxy || y - my < -maxy) | |
continue; | |
// We have to calculate distances in pixels, not in | |
// data units, because the scales of the axes may be different | |
var dx = Math.abs(axisx.p2c(x) - mouseX), | |
dy = Math.abs(axisy.p2c(y) - mouseY), | |
dist = dx * dx + dy * dy; // we save the sqrt | |
// use <= to ensure last point takes precedence | |
// (last generally means on top of) | |
if (dist < smallestDistance) { | |
smallestDistance = dist; | |
item = [i, j / ps]; | |
} | |
} | |
} | |
if (s.bars.show && !item) { // no other point can be nearby | |
var barLeft = s.bars.align == "left" ? 0 : -s.bars.barWidth/2, | |
barRight = barLeft + s.bars.barWidth; | |
for (j = 0; j < points.length; j += ps) { | |
var x = points[j], y = points[j + 1], b = points[j + 2]; | |
if (x == null) | |
continue; | |
// for a bar graph, the cursor must be inside the bar | |
if (series[i].bars.horizontal ? | |
(mx <= Math.max(b, x) && mx >= Math.min(b, x) && | |
my >= y + barLeft && my <= y + barRight) : | |
(mx >= x + barLeft && mx <= x + barRight && | |
my >= Math.min(b, y) && my <= Math.max(b, y))) | |
item = [i, j / ps]; | |
} | |
} | |
} | |
if (item) { | |
i = item[0]; | |
j = item[1]; | |
ps = series[i].datapoints.pointsize; | |
return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps), | |
dataIndex: j, | |
series: series[i], | |
seriesIndex: i }; | |
} | |
return null; | |
} | |
function onMouseMove(e) { | |
if (options.grid.hoverable) | |
triggerClickHoverEvent("plothover", e, | |
function (s) { return s["hoverable"] != false; }); | |
} | |
function onMouseLeave(e) { | |
if (options.grid.hoverable) | |
triggerClickHoverEvent("plothover", e, | |
function (s) { return false; }); | |
} | |
function onClick(e) { | |
triggerClickHoverEvent("plotclick", e, | |
function (s) { return s["clickable"] != false; }); | |
} | |
// trigger click or hover event (they send the same parameters | |
// so we share their code) | |
function triggerClickHoverEvent(eventname, event, seriesFilter) { | |
var offset = eventHolder.offset(), | |
canvasX = event.pageX - offset.left - plotOffset.left, | |
canvasY = event.pageY - offset.top - plotOffset.top, | |
pos = canvasToAxisCoords({ left: canvasX, top: canvasY }); | |
pos.pageX = event.pageX; | |
pos.pageY = event.pageY; | |
var item = findNearbyItem(canvasX, canvasY, seriesFilter); | |
if (item) { | |
// fill in mouse pos for any listeners out there | |
item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left); | |
item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top); | |
} | |
if (options.grid.autoHighlight) { | |
// clear auto-highlights | |
for (var i = 0; i < highlights.length; ++i) { | |
var h = highlights[i]; | |
if (h.auto == eventname && | |
!(item && h.series == item.series && | |
h.point[0] == item.datapoint[0] && | |
h.point[1] == item.datapoint[1])) | |
unhighlight(h.series, h.point); | |
} | |
if (item) | |
highlight(item.series, item.datapoint, eventname); | |
} | |
placeholder.trigger(eventname, [ pos, item ]); | |
} | |
function triggerRedrawOverlay() { | |
if (!redrawTimeout) | |
redrawTimeout = setTimeout(drawOverlay, 30); | |
} | |
function drawOverlay() { | |
redrawTimeout = null; | |
// draw highlights | |
octx.save(); | |
octx.clearRect(0, 0, canvasWidth, canvasHeight); | |
octx.translate(plotOffset.left, plotOffset.top); | |
var i, hi; | |
for (i = 0; i < highlights.length; ++i) { | |
hi = highlights[i]; | |
if (hi.series.bars.show) | |
drawBarHighlight(hi.series, hi.point); | |
else | |
drawPointHighlight(hi.series, hi.point); | |
} | |
octx.restore(); | |
executeHooks(hooks.drawOverlay, [octx]); | |
} | |
function highlight(s, point, auto) { | |
if (typeof s == "number") | |
s = series[s]; | |
if (typeof point == "number") { | |
var ps = s.datapoints.pointsize; | |
point = s.datapoints.points.slice(ps * point, ps * (point + 1)); | |
} | |
var i = indexOfHighlight(s, point); | |
if (i == -1) { | |
highlights.push({ series: s, point: point, auto: auto }); | |
triggerRedrawOverlay(); | |
} | |
else if (!auto) | |
highlights[i].auto = false; | |
} | |
function unhighlight(s, point) { | |
if (s == null && point == null) { | |
highlights = []; | |
triggerRedrawOverlay(); | |
} | |
if (typeof s == "number") | |
s = series[s]; | |
if (typeof point == "number") | |
point = s.data[point]; | |
var i = indexOfHighlight(s, point); | |
if (i != -1) { | |
highlights.splice(i, 1); | |
triggerRedrawOverlay(); | |
} | |
} | |
function indexOfHighlight(s, p) { | |
for (var i = 0; i < highlights.length; ++i) { | |
var h = highlights[i]; | |
if (h.series == s && h.point[0] == p[0] | |
&& h.point[1] == p[1]) | |
return i; | |
} | |
return -1; | |
} | |
function drawPointHighlight(series, point) { | |
var x = point[0], y = point[1], | |
axisx = series.xaxis, axisy = series.yaxis; | |
if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max) | |
return; | |
var pointRadius = series.points.radius + series.points.lineWidth / 2; | |
octx.lineWidth = pointRadius; | |
octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString(); | |
var radius = 1.5 * pointRadius, | |
x = axisx.p2c(x), | |
y = axisy.p2c(y); | |
octx.beginPath(); | |
if (series.points.symbol == "circle") | |
octx.arc(x, y, radius, 0, 2 * Math.PI, false); | |
else | |
series.points.symbol(octx, x, y, radius, false); | |
octx.closePath(); | |
octx.stroke(); | |
} | |
function drawBarHighlight(series, point) { | |
octx.lineWidth = series.bars.lineWidth; | |
octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString(); | |
var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString(); | |
var barLeft = series.bars.align == "left" ? 0 : -series.bars.barWidth/2; | |
drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth, | |
0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth); | |
} | |
function getColorOrGradient(spec, bottom, top, defaultColor) { | |
if (typeof spec == "string") | |
return spec; | |
else { | |
// assume this is a gradient spec; IE currently only | |
// supports a simple vertical gradient properly, so that's | |
// what we support too | |
var gradient = ctx.createLinearGradient(0, top, 0, bottom); | |
for (var i = 0, l = spec.colors.length; i < l; ++i) { | |
var c = spec.colors[i]; | |
if (typeof c != "string") { | |
var co = $.color.parse(defaultColor); | |
if (c.brightness != null) | |
co = co.scale('rgb', c.brightness) | |
if (c.opacity != null) | |
co.a *= c.opacity; | |
c = co.toString(); | |
} | |
gradient.addColorStop(i / (l - 1), c); | |
} | |
return gradient; | |
} | |
} | |
} | |
$.plot = function(placeholder, data, options) { | |
//var t0 = new Date(); | |
var plot = new Plot($(placeholder), data, options, $.plot.plugins); | |
//(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime())); | |
return plot; | |
}; | |
$.plot.version = "0.7"; | |
$.plot.plugins = []; | |
// returns a string with the date d formatted according to fmt | |
$.plot.formatDate = function(d, fmt, monthNames) { | |
var leftPad = function(n) { | |
n = "" + n; | |
return n.length == 1 ? "0" + n : n; | |
}; | |
var r = []; | |
var escape = false, padNext = false; | |
var hours = d.getUTCHours(); | |
var isAM = hours < 12; | |
if (monthNames == null) | |
monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; | |
if (fmt.search(/%p|%P/) != -1) { | |
if (hours > 12) { | |
hours = hours - 12; | |
} else if (hours == 0) { | |
hours = 12; | |
} | |
} | |
for (var i = 0; i < fmt.length; ++i) { | |
var c = fmt.charAt(i); | |
if (escape) { | |
switch (c) { | |
case 'h': c = "" + hours; break; | |
case 'H': c = leftPad(hours); break; | |
case 'M': c = leftPad(d.getUTCMinutes()); break; | |
case 'S': c = leftPad(d.getUTCSeconds()); break; | |
case 'd': c = "" + d.getUTCDate(); break; | |
case 'm': c = "" + (d.getUTCMonth() + 1); break; | |
case 'y': c = "" + d.getUTCFullYear(); break; | |
case 'b': c = "" + monthNames[d.getUTCMonth()]; break; | |
case 'p': c = (isAM) ? ("" + "am") : ("" + "pm"); break; | |
case 'P': c = (isAM) ? ("" + "AM") : ("" + "PM"); break; | |
case '0': c = ""; padNext = true; break; | |
} | |
if (c && padNext) { | |
c = leftPad(c); | |
padNext = false; | |
} | |
r.push(c); | |
if (!padNext) | |
escape = false; | |
} | |
else { | |
if (c == "%") | |
escape = true; | |
else | |
r.push(c); | |
} | |
} | |
return r.join(""); | |
}; | |
// round to nearby lower multiple of base | |
function floorInBase(n, base) { | |
return base * Math.floor(n / base); | |
} | |
})(jQuery); | |
/* Javascript plotting library for jQuery, v. 0.7. | |
* | |
* Released under the MIT license by IOLA, December 2007. | |
* | |
*/ | |
(function(b){b.color={};b.color.make=function(d,e,g,f){var c={};c.r=d||0;c.g=e||0;c.b=g||0;c.a=f!=null?f:1;c.add=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]+=j}return c.normalize()};c.scale=function(h,j){for(var k=0;k<h.length;++k){c[h.charAt(k)]*=j}return c.normalize()};c.toString=function(){if(c.a>=1){return"rgb("+[c.r,c.g,c.b].join(",")+")"}else{return"rgba("+[c.r,c.g,c.b,c.a].join(",")+")"}};c.normalize=function(){function h(k,j,l){return j<k?k:(j>l?l:j)}c.r=h(0,parseInt(c.r),255);c.g=h(0,parseInt(c.g),255);c.b=h(0,parseInt(c.b),255);c.a=h(0,c.a,1);return c};c.clone=function(){return b.color.make(c.r,c.b,c.g,c.a)};return c.normalize()};b.color.extract=function(d,e){var c;do{c=d.css(e).toLowerCase();if(c!=""&&c!="transparent"){break}d=d.parent()}while(!b.nodeName(d.get(0),"body"));if(c=="rgba(0, 0, 0, 0)"){c="transparent"}return b.color.parse(c)};b.color.parse=function(c){var d,f=b.color.make;if(d=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10))}if(d=/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(c)){return f(parseInt(d[1],10),parseInt(d[2],10),parseInt(d[3],10),parseFloat(d[4]))}if(d=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55)}if(d=/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(c)){return f(parseFloat(d[1])*2.55,parseFloat(d[2])*2.55,parseFloat(d[3])*2.55,parseFloat(d[4]))}if(d=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c)){return f(parseInt(d[1],16),parseInt(d[2],16),parseInt(d[3],16))}if(d=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c)){return f(parseInt(d[1]+d[1],16),parseInt(d[2]+d[2],16),parseInt(d[3]+d[3],16))}var e=b.trim(c).toLowerCase();if(e=="transparent"){return f(255,255,255,0)}else{d=a[e]||[0,0,0];return f(d[0],d[1],d[2])}};var a={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]}})(jQuery);(function(c){function b(av,ai,J,af){var Q=[],O={colors:["#edc240","#afd8f8","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{show:null,position:"bottom",mode:null,color:null,tickColor:null,transform:null,inverseTransform:null,min:null,max:null,autoscaleMargin:null,ticks:null,tickFormatter:null,labelWidth:null,labelHeight:null,reserveSpace:null,tickLength:null,alignTicksWithAxis:null,tickDecimals:null,tickSize:null,minTickSize:null,monthNames:null,timeformat:null,twelveHourClock:false},yaxis:{autoscaleMargin:0.02,position:"left"},xaxes:[],yaxes:[],series:{points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff",symbol:"circle"},lines:{lineWidth:2,fill:false,fillColor:null,steps:false},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,align:"left",horizontal:false},shadowSize:3},grid:{show:true,aboveData:false,color:"#545454",backgroundColor:null,borderColor:null,tickColor:null,labelMargin:5,axisMargin:8,borderWidth:2,minBorderMargin:null,markings:null,markingsColor:"#f4f4f4",markingsLineWidth:2,clickable:false,hoverable:false,autoHighlight:true,mouseActiveRadius:10},hooks:{}},az=null,ad=null,y=null,H=null,A=null,p=[],aw=[],q={left:0,right:0,top:0,bottom:0},G=0,I=0,h=0,w=0,ak={processOptions:[],processRawData:[],processDatapoints:[],drawSeries:[],draw:[],bindEvents:[],drawOverlay:[],shutdown:[]},aq=this;aq.setData=aj;aq.setupGrid=t;aq.draw=W;aq.getPlaceholder=function(){return av};aq.getCanvas=function(){return az};aq.getPlotOffset=function(){return q};aq.width=function(){return h};aq.height=function(){return w};aq.offset=function(){var aB=y.offset();aB.left+=q.left;aB.top+=q.top;return aB};aq.getData=function(){return Q};aq.getAxes=function(){var aC={},aB;c.each(p.concat(aw),function(aD,aE){if(aE){aC[aE.direction+(aE.n!=1?aE.n:"")+"axis"]=aE}});return aC};aq.getXAxes=function(){return p};aq.getYAxes=function(){return aw};aq.c2p=C;aq.p2c=ar;aq.getOptions=function(){return O};aq.highlight=x;aq.unhighlight=T;aq.triggerRedrawOverlay=f;aq.pointOffset=function(aB){return{left:parseInt(p[aA(aB,"x")-1].p2c(+aB.x)+q.left),top:parseInt(aw[aA(aB,"y")-1].p2c(+aB.y)+q.top)}};aq.shutdown=ag;aq.resize=function(){B();g(az);g(ad)};aq.hooks=ak;F(aq);Z(J);X();aj(ai);t();W();ah();function an(aD,aB){aB=[aq].concat(aB);for(var aC=0;aC<aD.length;++aC){aD[aC].apply(this,aB)}}function F(){for(var aB=0;aB<af.length;++aB){var aC=af[aB];aC.init(aq);if(aC.options){c.extend(true,O,aC.options)}}}function Z(aC){var aB;c.extend(true,O,aC);if(O.xaxis.color==null){O.xaxis.color=O.grid.color}if(O.yaxis.color==null){O.yaxis.color=O.grid.color}if(O.xaxis.tickColor==null){O.xaxis.tickColor=O.grid.tickColor}if(O.yaxis.tickColor==null){O.yaxis.tickColor=O.grid.tickColor}if(O.grid.borderColor==null){O.grid.borderColor=O.grid.color}if(O.grid.tickColor==null){O.grid.tickColor=c.color.parse(O.grid.color).scale("a",0.22).toString()}for(aB=0;aB<Math.max(1,O.xaxes.length);++aB){O.xaxes[aB]=c.extend(true,{},O.xaxis,O.xaxes[aB])}for(aB=0;aB<Math.max(1,O.yaxes.length);++aB){O.yaxes[aB]=c.extend(true,{},O.yaxis,O.yaxes[aB])}if(O.xaxis.noTicks&&O.xaxis.ticks==null){O.xaxis.ticks=O.xaxis.noTicks}if(O.yaxis.noTicks&&O.yaxis.ticks==null){O.yaxis.ticks=O.yaxis.noTicks}if(O.x2axis){O.xaxes[1]=c.extend(true,{},O.xaxis,O.x2axis);O.xaxes[1].position="top"}if(O.y2axis){O.yaxes[1]=c.extend(true,{},O.yaxis,O.y2axis);O.yaxes[1].position="right"}if(O.grid.coloredAreas){O.grid.markings=O.grid.coloredAreas}if(O.grid.coloredAreasColor){O.grid.markingsColor=O.grid.coloredAreasColor}if(O.lines){c.extend(true,O.series.lines,O.lines)}if(O.points){c.extend(true,O.series.points,O.points)}if(O.bars){c.extend(true,O.series.bars,O.bars)}if(O.shadowSize!=null){O.series.shadowSize=O.shadowSize}for(aB=0;aB<O.xaxes.length;++aB){V(p,aB+1).options=O.xaxes[aB]}for(aB=0;aB<O.yaxes.length;++aB){V(aw,aB+1).options=O.yaxes[aB]}for(var aD in ak){if(O.hooks[aD]&&O.hooks[aD].length){ak[aD]=ak[aD].concat(O.hooks[aD])}}an(ak.processOptions,[O])}function aj(aB){Q=Y(aB);ax();z()}function Y(aE){var aC=[];for(var aB=0;aB<aE.length;++aB){var aD=c.extend(true,{},O.series);if(aE[aB].data!=null){aD.data=aE[aB].data;delete aE[aB].data;c.extend(true,aD,aE[aB]);aE[aB].data=aD.data}else{aD.data=aE[aB]}aC.push(aD)}return aC}function aA(aC,aD){var aB=aC[aD+"axis"];if(typeof aB=="object"){aB=aB.n}if(typeof aB!="number"){aB=1}return aB}function m(){return c.grep(p.concat(aw),function(aB){return aB})}function C(aE){var aC={},aB,aD;for(aB=0;aB<p.length;++aB){aD=p[aB];if(aD&&aD.used){aC["x"+aD.n]=aD.c2p(aE.left)}}for(aB=0;aB<aw.length;++aB){aD=aw[aB];if(aD&&aD.used){aC["y"+aD.n]=aD.c2p(aE.top)}}if(aC.x1!==undefined){aC.x=aC.x1}if(aC.y1!==undefined){aC.y=aC.y1}return aC}function ar(aF){var aD={},aC,aE,aB;for(aC=0;aC<p.length;++aC){aE=p[aC];if(aE&&aE.used){aB="x"+aE.n;if(aF[aB]==null&&aE.n==1){aB="x"}if(aF[aB]!=null){aD.left=aE.p2c(aF[aB]);break}}}for(aC=0;aC<aw.length;++aC){aE=aw[aC];if(aE&&aE.used){aB="y"+aE.n;if(aF[aB]==null&&aE.n==1){aB="y"}if(aF[aB]!=null){aD.top=aE.p2c(aF[aB]);break}}}return aD}function V(aC,aB){if(!aC[aB-1]){aC[aB-1]={n:aB,direction:aC==p?"x":"y",options:c.extend(true,{},aC==p?O.xaxis:O.yaxis)}}return aC[aB-1]}function ax(){var aG;var aM=Q.length,aB=[],aE=[];for(aG=0;aG<Q.length;++aG){var aJ=Q[aG].color;if(aJ!=null){--aM;if(typeof aJ=="number"){aE.push(aJ)}else{aB.push(c.color.parse(Q[aG].color))}}}for(aG=0;aG<aE.length;++aG){aM=Math.max(aM,aE[aG]+1)}var aC=[],aF=0;aG=0;while(aC.length<aM){var aI;if(O.colors.length==aG){aI=c.color.make(100,100,100)}else{aI=c.color.parse(O.colors[aG])}var aD=aF%2==1?-1:1;aI.scale("rgb",1+aD*Math.ceil(aF/2)*0.2);aC.push(aI);++aG;if(aG>=O.colors.length){aG=0;++aF}}var aH=0,aN;for(aG=0;aG<Q.length;++aG){aN=Q[aG];if(aN.color==null){aN.color=aC[aH].toString();++aH}else{if(typeof aN.color=="number"){aN.color=aC[aN.color].toString()}}if(aN.lines.show==null){var aL,aK=true;for(aL in aN){if(aN[aL]&&aN[aL].show){aK=false;break}}if(aK){aN.lines.show=true}}aN.xaxis=V(p,aA(aN,"x"));aN.yaxis=V(aw,aA(aN,"y"))}}function z(){var aO=Number.POSITIVE_INFINITY,aI=Number.NEGATIVE_INFINITY,aB=Number.MAX_VALUE,aU,aS,aR,aN,aD,aJ,aT,aP,aH,aG,aC,a0,aX,aL;function aF(a3,a2,a1){if(a2<a3.datamin&&a2!=-aB){a3.datamin=a2}if(a1>a3.datamax&&a1!=aB){a3.datamax=a1}}c.each(m(),function(a1,a2){a2.datamin=aO;a2.datamax=aI;a2.used=false});for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aJ.datapoints={points:[]};an(ak.processRawData,[aJ,aJ.data,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];var aZ=aJ.data,aW=aJ.datapoints.format;if(!aW){aW=[];aW.push({x:true,number:true,required:true});aW.push({y:true,number:true,required:true});if(aJ.bars.show||(aJ.lines.show&&aJ.lines.fill)){aW.push({y:true,number:true,required:false,defaultValue:0});if(aJ.bars.horizontal){delete aW[aW.length-1].y;aW[aW.length-1].x=true}}aJ.datapoints.format=aW}if(aJ.datapoints.pointsize!=null){continue}aJ.datapoints.pointsize=aW.length;aP=aJ.datapoints.pointsize;aT=aJ.datapoints.points;insertSteps=aJ.lines.show&&aJ.lines.steps;aJ.xaxis.used=aJ.yaxis.used=true;for(aS=aR=0;aS<aZ.length;++aS,aR+=aP){aL=aZ[aS];var aE=aL==null;if(!aE){for(aN=0;aN<aP;++aN){a0=aL[aN];aX=aW[aN];if(aX){if(aX.number&&a0!=null){a0=+a0;if(isNaN(a0)){a0=null}else{if(a0==Infinity){a0=aB}else{if(a0==-Infinity){a0=-aB}}}}if(a0==null){if(aX.required){aE=true}if(aX.defaultValue!=null){a0=aX.defaultValue}}}aT[aR+aN]=a0}}if(aE){for(aN=0;aN<aP;++aN){a0=aT[aR+aN];if(a0!=null){aX=aW[aN];if(aX.x){aF(aJ.xaxis,a0,a0)}if(aX.y){aF(aJ.yaxis,a0,a0)}}aT[aR+aN]=null}}else{if(insertSteps&&aR>0&&aT[aR-aP]!=null&&aT[aR-aP]!=aT[aR]&&aT[aR-aP+1]!=aT[aR+1]){for(aN=0;aN<aP;++aN){aT[aR+aP+aN]=aT[aR+aN]}aT[aR+1]=aT[aR-aP+1];aR+=aP}}}}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];an(ak.processDatapoints,[aJ,aJ.datapoints])}for(aU=0;aU<Q.length;++aU){aJ=Q[aU];aT=aJ.datapoints.points,aP=aJ.datapoints.pointsize;var aK=aO,aQ=aO,aM=aI,aV=aI;for(aS=0;aS<aT.length;aS+=aP){if(aT[aS]==null){continue}for(aN=0;aN<aP;++aN){a0=aT[aS+aN];aX=aW[aN];if(!aX||a0==aB||a0==-aB){continue}if(aX.x){if(a0<aK){aK=a0}if(a0>aM){aM=a0}}if(aX.y){if(a0<aQ){aQ=a0}if(a0>aV){aV=a0}}}}if(aJ.bars.show){var aY=aJ.bars.align=="left"?0:-aJ.bars.barWidth/2;if(aJ.bars.horizontal){aQ+=aY;aV+=aY+aJ.bars.barWidth}else{aK+=aY;aM+=aY+aJ.bars.barWidth}}aF(aJ.xaxis,aK,aM);aF(aJ.yaxis,aQ,aV)}c.each(m(),function(a1,a2){if(a2.datamin==aO){a2.datamin=null}if(a2.datamax==aI){a2.datamax=null}})}function j(aB,aC){var aD=document.createElement("canvas");aD.className=aC;aD.width=G;aD.height=I;if(!aB){c(aD).css({position:"absolute",left:0,top:0})}c(aD).appendTo(av);if(!aD.getContext){aD=window.G_vmlCanvasManager.initElement(aD)}aD.getContext("2d").save();return aD}function B(){G=av.width();I=av.height();if(G<=0||I<=0){throw"Invalid dimensions for plot, width = "+G+", height = "+I}}function g(aC){if(aC.width!=G){aC.width=G}if(aC.height!=I){aC.height=I}var aB=aC.getContext("2d");aB.restore();aB.save()}function X(){var aC,aB=av.children("canvas.base"),aD=av.children("canvas.overlay");if(aB.length==0||aD==0){av.html("");av.css({padding:0});if(av.css("position")=="static"){av.css("position","relative")}B();az=j(true,"base");ad=j(false,"overlay");aC=false}else{az=aB.get(0);ad=aD.get(0);aC=true}H=az.getContext("2d");A=ad.getContext("2d");y=c([ad,az]);if(aC){av.data("plot").shutdown();aq.resize();A.clearRect(0,0,G,I);y.unbind();av.children().not([az,ad]).remove()}av.data("plot",aq)}function ah(){if(O.grid.hoverable){y.mousemove(aa);y.mouseleave(l)}if(O.grid.clickable){y.click(R)}an(ak.bindEvents,[y])}function ag(){if(M){clearTimeout(M)}y.unbind("mousemove",aa);y.unbind("mouseleave",l);y.unbind("click",R);an(ak.shutdown,[y])}function r(aG){function aC(aH){return aH}var aF,aB,aD=aG.options.transform||aC,aE=aG.options.inverseTransform;if(aG.direction=="x"){aF=aG.scale=h/Math.abs(aD(aG.max)-aD(aG.min));aB=Math.min(aD(aG.max),aD(aG.min))}else{aF=aG.scale=w/Math.abs(aD(aG.max)-aD(aG.min));aF=-aF;aB=Math.max(aD(aG.max),aD(aG.min))}if(aD==aC){aG.p2c=function(aH){return(aH-aB)*aF}}else{aG.p2c=function(aH){return(aD(aH)-aB)*aF}}if(!aE){aG.c2p=function(aH){return aB+aH/aF}}else{aG.c2p=function(aH){return aE(aB+aH/aF)}}}function L(aD){var aB=aD.options,aF,aJ=aD.ticks||[],aI=[],aE,aK=aB.labelWidth,aG=aB.labelHeight,aC;function aH(aM,aL){return c('<div style="position:absolute;top:-10000px;'+aL+'font-size:smaller"><div class="'+aD.direction+"Axis "+aD.direction+aD.n+'Axis">'+aM.join("")+"</div></div>").appendTo(av)}if(aD.direction=="x"){if(aK==null){aK=Math.floor(G/(aJ.length>0?aJ.length:1))}if(aG==null){aI=[];for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel" style="float:left;width:'+aK+'px">'+aE+"</div>")}}if(aI.length>0){aI.push('<div style="clear:left"></div>');aC=aH(aI,"width:10000px;");aG=aC.height();aC.remove()}}}else{if(aK==null||aG==null){for(aF=0;aF<aJ.length;++aF){aE=aJ[aF].label;if(aE){aI.push('<div class="tickLabel">'+aE+"</div>")}}if(aI.length>0){aC=aH(aI,"");if(aK==null){aK=aC.children().width()}if(aG==null){aG=aC.find("div.tickLabel").height()}aC.remove()}}}if(aK==null){aK=0}if(aG==null){aG=0}aD.labelWidth=aK;aD.labelHeight=aG}function au(aD){var aC=aD.labelWidth,aL=aD.labelHeight,aH=aD.options.position,aF=aD.options.tickLength,aG=O.grid.axisMargin,aJ=O.grid.labelMargin,aK=aD.direction=="x"?p:aw,aE;var aB=c.grep(aK,function(aN){return aN&&aN.options.position==aH&&aN.reserveSpace});if(c.inArray(aD,aB)==aB.length-1){aG=0}if(aF==null){aF="full"}var aI=c.grep(aK,function(aN){return aN&&aN.reserveSpace});var aM=c.inArray(aD,aI)==0;if(!aM&&aF=="full"){aF=5}if(!isNaN(+aF)){aJ+=+aF}if(aD.direction=="x"){aL+=aJ;if(aH=="bottom"){q.bottom+=aL+aG;aD.box={top:I-q.bottom,height:aL}}else{aD.box={top:q.top+aG,height:aL};q.top+=aL+aG}}else{aC+=aJ;if(aH=="left"){aD.box={left:q.left+aG,width:aC};q.left+=aC+aG}else{q.right+=aC+aG;aD.box={left:G-q.right,width:aC}}}aD.position=aH;aD.tickLength=aF;aD.box.padding=aJ;aD.innermost=aM}function U(aB){if(aB.direction=="x"){aB.box.left=q.left;aB.box.width=h}else{aB.box.top=q.top;aB.box.height=w}}function t(){var aC,aE=m();c.each(aE,function(aF,aG){aG.show=aG.options.show;if(aG.show==null){aG.show=aG.used}aG.reserveSpace=aG.show||aG.options.reserveSpace;n(aG)});allocatedAxes=c.grep(aE,function(aF){return aF.reserveSpace});q.left=q.right=q.top=q.bottom=0;if(O.grid.show){c.each(allocatedAxes,function(aF,aG){S(aG);P(aG);ap(aG,aG.ticks);L(aG)});for(aC=allocatedAxes.length-1;aC>=0;--aC){au(allocatedAxes[aC])}var aD=O.grid.minBorderMargin;if(aD==null){aD=0;for(aC=0;aC<Q.length;++aC){aD=Math.max(aD,Q[aC].points.radius+Q[aC].points.lineWidth/2)}}for(var aB in q){q[aB]+=O.grid.borderWidth;q[aB]=Math.max(aD,q[aB])}}h=G-q.left-q.right;w=I-q.bottom-q.top;c.each(aE,function(aF,aG){r(aG)});if(O.grid.show){c.each(allocatedAxes,function(aF,aG){U(aG)});k()}o()}function n(aE){var aF=aE.options,aD=+(aF.min!=null?aF.min:aE.datamin),aB=+(aF.max!=null?aF.max:aE.datamax),aH=aB-aD;if(aH==0){var aC=aB==0?1:0.01;if(aF.min==null){aD-=aC}if(aF.max==null||aF.min!=null){aB+=aC}}else{var aG=aF.autoscaleMargin;if(aG!=null){if(aF.min==null){aD-=aH*aG;if(aD<0&&aE.datamin!=null&&aE.datamin>=0){aD=0}}if(aF.max==null){aB+=aH*aG;if(aB>0&&aE.datamax!=null&&aE.datamax<=0){aB=0}}}}aE.min=aD;aE.max=aB}function S(aG){var aM=aG.options;var aH;if(typeof aM.ticks=="number"&&aM.ticks>0){aH=aM.ticks}else{aH=0.3*Math.sqrt(aG.direction=="x"?G:I)}var aT=(aG.max-aG.min)/aH,aO,aB,aN,aR,aS,aQ,aI;if(aM.mode=="time"){var aJ={second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000};var aK=[[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"]];var aC=0;if(aM.minTickSize!=null){if(typeof aM.tickSize=="number"){aC=aM.tickSize}else{aC=aM.minTickSize[0]*aJ[aM.minTickSize[1]]}}for(var aS=0;aS<aK.length-1;++aS){if(aT<(aK[aS][0]*aJ[aK[aS][1]]+aK[aS+1][0]*aJ[aK[aS+1][1]])/2&&aK[aS][0]*aJ[aK[aS][1]]>=aC){break}}aO=aK[aS][0];aN=aK[aS][1];if(aN=="year"){aQ=Math.pow(10,Math.floor(Math.log(aT/aJ.year)/Math.LN10));aI=(aT/aJ.year)/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ}aG.tickSize=aM.tickSize||[aO,aN];aB=function(aX){var a2=[],a0=aX.tickSize[0],a3=aX.tickSize[1],a1=new Date(aX.min);var aW=a0*aJ[a3];if(a3=="second"){a1.setUTCSeconds(a(a1.getUTCSeconds(),a0))}if(a3=="minute"){a1.setUTCMinutes(a(a1.getUTCMinutes(),a0))}if(a3=="hour"){a1.setUTCHours(a(a1.getUTCHours(),a0))}if(a3=="month"){a1.setUTCMonth(a(a1.getUTCMonth(),a0))}if(a3=="year"){a1.setUTCFullYear(a(a1.getUTCFullYear(),a0))}a1.setUTCMilliseconds(0);if(aW>=aJ.minute){a1.setUTCSeconds(0)}if(aW>=aJ.hour){a1.setUTCMinutes(0)}if(aW>=aJ.day){a1.setUTCHours(0)}if(aW>=aJ.day*4){a1.setUTCDate(1)}if(aW>=aJ.year){a1.setUTCMonth(0)}var a5=0,a4=Number.NaN,aY;do{aY=a4;a4=a1.getTime();a2.push(a4);if(a3=="month"){if(a0<1){a1.setUTCDate(1);var aV=a1.getTime();a1.setUTCMonth(a1.getUTCMonth()+1);var aZ=a1.getTime();a1.setTime(a4+a5*aJ.hour+(aZ-aV)*a0);a5=a1.getUTCHours();a1.setUTCHours(0)}else{a1.setUTCMonth(a1.getUTCMonth()+a0)}}else{if(a3=="year"){a1.setUTCFullYear(a1.getUTCFullYear()+a0)}else{a1.setTime(a4+aW)}}}while(a4<aX.max&&a4!=aY);return a2};aR=function(aV,aY){var a0=new Date(aV);if(aM.timeformat!=null){return c.plot.formatDate(a0,aM.timeformat,aM.monthNames)}var aW=aY.tickSize[0]*aJ[aY.tickSize[1]];var aX=aY.max-aY.min;var aZ=(aM.twelveHourClock)?" %p":"";if(aW<aJ.minute){fmt="%h:%M:%S"+aZ}else{if(aW<aJ.day){if(aX<2*aJ.day){fmt="%h:%M"+aZ}else{fmt="%b %d %h:%M"+aZ}}else{if(aW<aJ.month){fmt="%b %d"}else{if(aW<aJ.year){if(aX<aJ.year){fmt="%b"}else{fmt="%b %y"}}else{fmt="%y"}}}}return c.plot.formatDate(a0,fmt,aM.monthNames)}}else{var aU=aM.tickDecimals;var aP=-Math.floor(Math.log(aT)/Math.LN10);if(aU!=null&&aP>aU){aP=aU}aQ=Math.pow(10,-aP);aI=aT/aQ;if(aI<1.5){aO=1}else{if(aI<3){aO=2;if(aI>2.25&&(aU==null||aP+1<=aU)){aO=2.5;++aP}}else{if(aI<7.5){aO=5}else{aO=10}}}aO*=aQ;if(aM.minTickSize!=null&&aO<aM.minTickSize){aO=aM.minTickSize}aG.tickDecimals=Math.max(0,aU!=null?aU:aP);aG.tickSize=aM.tickSize||aO;aB=function(aX){var aZ=[];var a0=a(aX.min,aX.tickSize),aW=0,aV=Number.NaN,aY;do{aY=aV;aV=a0+aW*aX.tickSize;aZ.push(aV);++aW}while(aV<aX.max&&aV!=aY);return aZ};aR=function(aV,aW){return aV.toFixed(aW.tickDecimals)}}if(aM.alignTicksWithAxis!=null){var aF=(aG.direction=="x"?p:aw)[aM.alignTicksWithAxis-1];if(aF&&aF.used&&aF!=aG){var aL=aB(aG);if(aL.length>0){if(aM.min==null){aG.min=Math.min(aG.min,aL[0])}if(aM.max==null&&aL.length>1){aG.max=Math.max(aG.max,aL[aL.length-1])}}aB=function(aX){var aY=[],aV,aW;for(aW=0;aW<aF.ticks.length;++aW){aV=(aF.ticks[aW].v-aF.min)/(aF.max-aF.min);aV=aX.min+aV*(aX.max-aX.min);aY.push(aV)}return aY};if(aG.mode!="time"&&aM.tickDecimals==null){var aE=Math.max(0,-Math.floor(Math.log(aT)/Math.LN10)+1),aD=aB(aG);if(!(aD.length>1&&/\..*0$/.test((aD[1]-aD[0]).toFixed(aE)))){aG.tickDecimals=aE}}}}aG.tickGenerator=aB;if(c.isFunction(aM.tickFormatter)){aG.tickFormatter=function(aV,aW){return""+aM.tickFormatter(aV,aW)}}else{aG.tickFormatter=aR}}function P(aF){var aH=aF.options.ticks,aG=[];if(aH==null||(typeof aH=="number"&&aH>0)){aG=aF.tickGenerator(aF)}else{if(aH){if(c.isFunction(aH)){aG=aH({min:aF.min,max:aF.max})}else{aG=aH}}}var aE,aB;aF.ticks=[];for(aE=0;aE<aG.length;++aE){var aC=null;var aD=aG[aE];if(typeof aD=="object"){aB=+aD[0];if(aD.length>1){aC=aD[1]}}else{aB=+aD}if(aC==null){aC=aF.tickFormatter(aB,aF)}if(!isNaN(aB)){aF.ticks.push({v:aB,label:aC})}}}function ap(aB,aC){if(aB.options.autoscaleMargin&&aC.length>0){if(aB.options.min==null){aB.min=Math.min(aB.min,aC[0].v)}if(aB.options.max==null&&aC.length>1){aB.max=Math.max(aB.max,aC[aC.length-1].v)}}}function W(){H.clearRect(0,0,G,I);var aC=O.grid;if(aC.show&&aC.backgroundColor){N()}if(aC.show&&!aC.aboveData){ac()}for(var aB=0;aB<Q.length;++aB){an(ak.drawSeries,[H,Q[aB]]);d(Q[aB])}an(ak.draw,[H]);if(aC.show&&aC.aboveData){ac()}}function D(aB,aI){var aE,aH,aG,aD,aF=m();for(i=0;i<aF.length;++i){aE=aF[i];if(aE.direction==aI){aD=aI+aE.n+"axis";if(!aB[aD]&&aE.n==1){aD=aI+"axis"}if(aB[aD]){aH=aB[aD].from;aG=aB[aD].to;break}}}if(!aB[aD]){aE=aI=="x"?p[0]:aw[0];aH=aB[aI+"1"];aG=aB[aI+"2"]}if(aH!=null&&aG!=null&&aH>aG){var aC=aH;aH=aG;aG=aC}return{from:aH,to:aG,axis:aE}}function N(){H.save();H.translate(q.left,q.top);H.fillStyle=am(O.grid.backgroundColor,w,0,"rgba(255, 255, 255, 0)");H.fillRect(0,0,h,w);H.restore()}function ac(){var aF;H.save();H.translate(q.left,q.top);var aH=O.grid.markings;if(aH){if(c.isFunction(aH)){var aK=aq.getAxes();aK.xmin=aK.xaxis.min;aK.xmax=aK.xaxis.max;aK.ymin=aK.yaxis.min;aK.ymax=aK.yaxis.max;aH=aH(aK)}for(aF=0;aF<aH.length;++aF){var aD=aH[aF],aC=D(aD,"x"),aI=D(aD,"y");if(aC.from==null){aC.from=aC.axis.min}if(aC.to==null){aC.to=aC.axis.max}if(aI.from==null){aI.from=aI.axis.min}if(aI.to==null){aI.to=aI.axis.max}if(aC.to<aC.axis.min||aC.from>aC.axis.max||aI.to<aI.axis.min||aI.from>aI.axis.max){continue}aC.from=Math.max(aC.from,aC.axis.min);aC.to=Math.min(aC.to,aC.axis.max);aI.from=Math.max(aI.from,aI.axis.min);aI.to=Math.min(aI.to,aI.axis.max);if(aC.from==aC.to&&aI.from==aI.to){continue}aC.from=aC.axis.p2c(aC.from);aC.to=aC.axis.p2c(aC.to);aI.from=aI.axis.p2c(aI.from);aI.to=aI.axis.p2c(aI.to);if(aC.from==aC.to||aI.from==aI.to){H.beginPath();H.strokeStyle=aD.color||O.grid.markingsColor;H.lineWidth=aD.lineWidth||O.grid.markingsLineWidth;H.moveTo(aC.from,aI.from);H.lineTo(aC.to,aI.to);H.stroke()}else{H.fillStyle=aD.color||O.grid.markingsColor;H.fillRect(aC.from,aI.to,aC.to-aC.from,aI.from-aI.to)}}}var aK=m(),aM=O.grid.borderWidth;for(var aE=0;aE<aK.length;++aE){var aB=aK[aE],aG=aB.box,aQ=aB.tickLength,aN,aL,aP,aJ;if(!aB.show||aB.ticks.length==0){continue}H.strokeStyle=aB.options.tickColor||c.color.parse(aB.options.color).scale("a",0.22).toString();H.lineWidth=1;if(aB.direction=="x"){aN=0;if(aQ=="full"){aL=(aB.position=="top"?0:w)}else{aL=aG.top-q.top+(aB.position=="top"?aG.height:0)}}else{aL=0;if(aQ=="full"){aN=(aB.position=="left"?0:h)}else{aN=aG.left-q.left+(aB.position=="left"?aG.width:0)}}if(!aB.innermost){H.beginPath();aP=aJ=0;if(aB.direction=="x"){aP=h}else{aJ=w}if(H.lineWidth==1){aN=Math.floor(aN)+0.5;aL=Math.floor(aL)+0.5}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ);H.stroke()}H.beginPath();for(aF=0;aF<aB.ticks.length;++aF){var aO=aB.ticks[aF].v;aP=aJ=0;if(aO<aB.min||aO>aB.max||(aQ=="full"&&aM>0&&(aO==aB.min||aO==aB.max))){continue}if(aB.direction=="x"){aN=aB.p2c(aO);aJ=aQ=="full"?-w:aQ;if(aB.position=="top"){aJ=-aJ}}else{aL=aB.p2c(aO);aP=aQ=="full"?-h:aQ;if(aB.position=="left"){aP=-aP}}if(H.lineWidth==1){if(aB.direction=="x"){aN=Math.floor(aN)+0.5}else{aL=Math.floor(aL)+0.5}}H.moveTo(aN,aL);H.lineTo(aN+aP,aL+aJ)}H.stroke()}if(aM){H.lineWidth=aM;H.strokeStyle=O.grid.borderColor;H.strokeRect(-aM/2,-aM/2,h+aM,w+aM)}H.restore()}function k(){av.find(".tickLabels").remove();var aG=['<div class="tickLabels" style="font-size:smaller">'];var aJ=m();for(var aD=0;aD<aJ.length;++aD){var aC=aJ[aD],aF=aC.box;if(!aC.show){continue}aG.push('<div class="'+aC.direction+"Axis "+aC.direction+aC.n+'Axis" style="color:'+aC.options.color+'">');for(var aE=0;aE<aC.ticks.length;++aE){var aH=aC.ticks[aE];if(!aH.label||aH.v<aC.min||aH.v>aC.max){continue}var aK={},aI;if(aC.direction=="x"){aI="center";aK.left=Math.round(q.left+aC.p2c(aH.v)-aC.labelWidth/2);if(aC.position=="bottom"){aK.top=aF.top+aF.padding}else{aK.bottom=I-(aF.top+aF.height-aF.padding)}}else{aK.top=Math.round(q.top+aC.p2c(aH.v)-aC.labelHeight/2);if(aC.position=="left"){aK.right=G-(aF.left+aF.width-aF.padding);aI="right"}else{aK.left=aF.left+aF.padding;aI="left"}}aK.width=aC.labelWidth;var aB=["position:absolute","text-align:"+aI];for(var aL in aK){aB.push(aL+":"+aK[aL]+"px")}aG.push('<div class="tickLabel" style="'+aB.join(";")+'">'+aH.label+"</div>")}aG.push("</div>")}aG.push("</div>");av.append(aG.join(""))}function d(aB){if(aB.lines.show){at(aB)}if(aB.bars.show){e(aB)}if(aB.points.show){ao(aB)}}function at(aE){function aD(aP,aQ,aI,aU,aT){var aV=aP.points,aJ=aP.pointsize,aN=null,aM=null;H.beginPath();for(var aO=aJ;aO<aV.length;aO+=aJ){var aL=aV[aO-aJ],aS=aV[aO-aJ+1],aK=aV[aO],aR=aV[aO+1];if(aL==null||aK==null){continue}if(aS<=aR&&aS<aT.min){if(aR<aT.min){continue}aL=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.min}else{if(aR<=aS&&aR<aT.min){if(aS<aT.min){continue}aK=(aT.min-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.min}}if(aS>=aR&&aS>aT.max){if(aR>aT.max){continue}aL=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aS=aT.max}else{if(aR>=aS&&aR>aT.max){if(aS>aT.max){continue}aK=(aT.max-aS)/(aR-aS)*(aK-aL)+aL;aR=aT.max}}if(aL<=aK&&aL<aU.min){if(aK<aU.min){continue}aS=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.min}else{if(aK<=aL&&aK<aU.min){if(aL<aU.min){continue}aR=(aU.min-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.min}}if(aL>=aK&&aL>aU.max){if(aK>aU.max){continue}aS=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aL=aU.max}else{if(aK>=aL&&aK>aU.max){if(aL>aU.max){continue}aR=(aU.max-aL)/(aK-aL)*(aR-aS)+aS;aK=aU.max}}if(aL!=aN||aS!=aM){H.moveTo(aU.p2c(aL)+aQ,aT.p2c(aS)+aI)}aN=aK;aM=aR;H.lineTo(aU.p2c(aK)+aQ,aT.p2c(aR)+aI)}H.stroke()}function aF(aI,aQ,aP){var aW=aI.points,aV=aI.pointsize,aN=Math.min(Math.max(0,aP.min),aP.max),aX=0,aU,aT=false,aM=1,aL=0,aR=0;while(true){if(aV>0&&aX>aW.length+aV){break}aX+=aV;var aZ=aW[aX-aV],aK=aW[aX-aV+aM],aY=aW[aX],aJ=aW[aX+aM];if(aT){if(aV>0&&aZ!=null&&aY==null){aR=aX;aV=-aV;aM=2;continue}if(aV<0&&aX==aL+aV){H.fill();aT=false;aV=-aV;aM=1;aX=aL=aR+aV;continue}}if(aZ==null||aY==null){continue}if(aZ<=aY&&aZ<aQ.min){if(aY<aQ.min){continue}aK=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.min}else{if(aY<=aZ&&aY<aQ.min){if(aZ<aQ.min){continue}aJ=(aQ.min-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.min}}if(aZ>=aY&&aZ>aQ.max){if(aY>aQ.max){continue}aK=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aZ=aQ.max}else{if(aY>=aZ&&aY>aQ.max){if(aZ>aQ.max){continue}aJ=(aQ.max-aZ)/(aY-aZ)*(aJ-aK)+aK;aY=aQ.max}}if(!aT){H.beginPath();H.moveTo(aQ.p2c(aZ),aP.p2c(aN));aT=true}if(aK>=aP.max&&aJ>=aP.max){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.max));H.lineTo(aQ.p2c(aY),aP.p2c(aP.max));continue}else{if(aK<=aP.min&&aJ<=aP.min){H.lineTo(aQ.p2c(aZ),aP.p2c(aP.min));H.lineTo(aQ.p2c(aY),aP.p2c(aP.min));continue}}var aO=aZ,aS=aY;if(aK<=aJ&&aK<aP.min&&aJ>=aP.min){aZ=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.min}else{if(aJ<=aK&&aJ<aP.min&&aK>=aP.min){aY=(aP.min-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.min}}if(aK>=aJ&&aK>aP.max&&aJ<=aP.max){aZ=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aK=aP.max}else{if(aJ>=aK&&aJ>aP.max&&aK<=aP.max){aY=(aP.max-aK)/(aJ-aK)*(aY-aZ)+aZ;aJ=aP.max}}if(aZ!=aO){H.lineTo(aQ.p2c(aO),aP.p2c(aK))}H.lineTo(aQ.p2c(aZ),aP.p2c(aK));H.lineTo(aQ.p2c(aY),aP.p2c(aJ));if(aY!=aS){H.lineTo(aQ.p2c(aY),aP.p2c(aJ));H.lineTo(aQ.p2c(aS),aP.p2c(aJ))}}}H.save();H.translate(q.left,q.top);H.lineJoin="round";var aG=aE.lines.lineWidth,aB=aE.shadowSize;if(aG>0&&aB>0){H.lineWidth=aB;H.strokeStyle="rgba(0,0,0,0.1)";var aH=Math.PI/18;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/2),Math.cos(aH)*(aG/2+aB/2),aE.xaxis,aE.yaxis);H.lineWidth=aB/2;aD(aE.datapoints,Math.sin(aH)*(aG/2+aB/4),Math.cos(aH)*(aG/2+aB/4),aE.xaxis,aE.yaxis)}H.lineWidth=aG;H.strokeStyle=aE.color;var aC=ae(aE.lines,aE.color,0,w);if(aC){H.fillStyle=aC;aF(aE.datapoints,aE.xaxis,aE.yaxis)}if(aG>0){aD(aE.datapoints,0,0,aE.xaxis,aE.yaxis)}H.restore()}function ao(aE){function aH(aN,aM,aU,aK,aS,aT,aQ,aJ){var aR=aN.points,aI=aN.pointsize;for(var aL=0;aL<aR.length;aL+=aI){var aP=aR[aL],aO=aR[aL+1];if(aP==null||aP<aT.min||aP>aT.max||aO<aQ.min||aO>aQ.max){continue}H.beginPath();aP=aT.p2c(aP);aO=aQ.p2c(aO)+aK;if(aJ=="circle"){H.arc(aP,aO,aM,0,aS?Math.PI:Math.PI*2,false)}else{aJ(H,aP,aO,aM,aS)}H.closePath();if(aU){H.fillStyle=aU;H.fill()}H.stroke()}}H.save();H.translate(q.left,q.top);var aG=aE.points.lineWidth,aC=aE.shadowSize,aB=aE.points.radius,aF=aE.points.symbol;if(aG>0&&aC>0){var aD=aC/2;H.lineWidth=aD;H.strokeStyle="rgba(0,0,0,0.1)";aH(aE.datapoints,aB,null,aD+aD/2,true,aE.xaxis,aE.yaxis,aF);H.strokeStyle="rgba(0,0,0,0.2)";aH(aE.datapoints,aB,null,aD/2,true,aE.xaxis,aE.yaxis,aF)}H.lineWidth=aG;H.strokeStyle=aE.color;aH(aE.datapoints,aB,ae(aE.points,aE.color),0,false,aE.xaxis,aE.yaxis,aF);H.restore()}function E(aN,aM,aV,aI,aQ,aF,aD,aL,aK,aU,aR,aC){var aE,aT,aJ,aP,aG,aB,aO,aH,aS;if(aR){aH=aB=aO=true;aG=false;aE=aV;aT=aN;aP=aM+aI;aJ=aM+aQ;if(aT<aE){aS=aT;aT=aE;aE=aS;aG=true;aB=false}}else{aG=aB=aO=true;aH=false;aE=aN+aI;aT=aN+aQ;aJ=aV;aP=aM;if(aP<aJ){aS=aP;aP=aJ;aJ=aS;aH=true;aO=false}}if(aT<aL.min||aE>aL.max||aP<aK.min||aJ>aK.max){return}if(aE<aL.min){aE=aL.min;aG=false}if(aT>aL.max){aT=aL.max;aB=false}if(aJ<aK.min){aJ=aK.min;aH=false}if(aP>aK.max){aP=aK.max;aO=false}aE=aL.p2c(aE);aJ=aK.p2c(aJ);aT=aL.p2c(aT);aP=aK.p2c(aP);if(aD){aU.beginPath();aU.moveTo(aE,aJ);aU.lineTo(aE,aP);aU.lineTo(aT,aP);aU.lineTo(aT,aJ);aU.fillStyle=aD(aJ,aP);aU.fill()}if(aC>0&&(aG||aB||aO||aH)){aU.beginPath();aU.moveTo(aE,aJ+aF);if(aG){aU.lineTo(aE,aP+aF)}else{aU.moveTo(aE,aP+aF)}if(aO){aU.lineTo(aT,aP+aF)}else{aU.moveTo(aT,aP+aF)}if(aB){aU.lineTo(aT,aJ+aF)}else{aU.moveTo(aT,aJ+aF)}if(aH){aU.lineTo(aE,aJ+aF)}else{aU.moveTo(aE,aJ+aF)}aU.stroke()}}function e(aD){function aC(aJ,aI,aL,aG,aK,aN,aM){var aO=aJ.points,aF=aJ.pointsize;for(var aH=0;aH<aO.length;aH+=aF){if(aO[aH]==null){continue}E(aO[aH],aO[aH+1],aO[aH+2],aI,aL,aG,aK,aN,aM,H,aD.bars.horizontal,aD.bars.lineWidth)}}H.save();H.translate(q.left,q.top);H.lineWidth=aD.bars.lineWidth;H.strokeStyle=aD.color;var aB=aD.bars.align=="left"?0:-aD.bars.barWidth/2;var aE=aD.bars.fill?function(aF,aG){return ae(aD.bars,aD.color,aF,aG)}:null;aC(aD.datapoints,aB,aB+aD.bars.barWidth,0,aE,aD.xaxis,aD.yaxis);H.restore()}function ae(aD,aB,aC,aF){var aE=aD.fill;if(!aE){return null}if(aD.fillColor){return am(aD.fillColor,aC,aF,aB)}var aG=c.color.parse(aB);aG.a=typeof aE=="number"?aE:0.4;aG.normalize();return aG.toString()}function o(){av.find(".legend").remove();if(!O.legend.show){return}var aH=[],aF=false,aN=O.legend.labelFormatter,aM,aJ;for(var aE=0;aE<Q.length;++aE){aM=Q[aE];aJ=aM.label;if(!aJ){continue}if(aE%O.legend.noColumns==0){if(aF){aH.push("</tr>")}aH.push("<tr>");aF=true}if(aN){aJ=aN(aJ,aM)}aH.push('<td class="legendColorBox"><div style="border:1px solid '+O.legend.labelBoxBorderColor+';padding:1px"><div style="width:4px;height:0;border:5px solid '+aM.color+';overflow:hidden"></div></div></td><td class="legendLabel">'+aJ+"</td>")}if(aF){aH.push("</tr>")}if(aH.length==0){return}var aL='<table style="font-size:smaller;color:'+O.grid.color+'">'+aH.join("")+"</table>";if(O.legend.container!=null){c(O.legend.container).html(aL)}else{var aI="",aC=O.legend.position,aD=O.legend.margin;if(aD[0]==null){aD=[aD,aD]}if(aC.charAt(0)=="n"){aI+="top:"+(aD[1]+q.top)+"px;"}else{if(aC.charAt(0)=="s"){aI+="bottom:"+(aD[1]+q.bottom)+"px;"}}if(aC.charAt(1)=="e"){aI+="right:"+(aD[0]+q.right)+"px;"}else{if(aC.charAt(1)=="w"){aI+="left:"+(aD[0]+q.left)+"px;"}}var aK=c('<div class="legend">'+aL.replace('style="','style="position:absolute;'+aI+";")+"</div>").appendTo(av);if(O.legend.backgroundOpacity!=0){var aG=O.legend.backgroundColor;if(aG==null){aG=O.grid.backgroundColor;if(aG&&typeof aG=="string"){aG=c.color.parse(aG)}else{aG=c.color.extract(aK,"background-color")}aG.a=1;aG=aG.toString()}var aB=aK.children();c('<div style="position:absolute;width:'+aB.width()+"px;height:"+aB.height()+"px;"+aI+"background-color:"+aG+';"> </div>').prependTo(aK).css("opacity",O.legend.backgroundOpacity)}}}var ab=[],M=null;function K(aI,aG,aD){var aO=O.grid.mouseActiveRadius,a0=aO*aO+1,aY=null,aR=false,aW,aU;for(aW=Q.length-1;aW>=0;--aW){if(!aD(Q[aW])){continue}var aP=Q[aW],aH=aP.xaxis,aF=aP.yaxis,aV=aP.datapoints.points,aT=aP.datapoints.pointsize,aQ=aH.c2p(aI),aN=aF.c2p(aG),aC=aO/aH.scale,aB=aO/aF.scale;if(aH.options.inverseTransform){aC=Number.MAX_VALUE}if(aF.options.inverseTransform){aB=Number.MAX_VALUE}if(aP.lines.show||aP.points.show){for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1];if(aK==null){continue}if(aK-aQ>aC||aK-aQ<-aC||aJ-aN>aB||aJ-aN<-aB){continue}var aM=Math.abs(aH.p2c(aK)-aI),aL=Math.abs(aF.p2c(aJ)-aG),aS=aM*aM+aL*aL;if(aS<a0){a0=aS;aY=[aW,aU/aT]}}}if(aP.bars.show&&!aY){var aE=aP.bars.align=="left"?0:-aP.bars.barWidth/2,aX=aE+aP.bars.barWidth;for(aU=0;aU<aV.length;aU+=aT){var aK=aV[aU],aJ=aV[aU+1],aZ=aV[aU+2];if(aK==null){continue}if(Q[aW].bars.horizontal?(aQ<=Math.max(aZ,aK)&&aQ>=Math.min(aZ,aK)&&aN>=aJ+aE&&aN<=aJ+aX):(aQ>=aK+aE&&aQ<=aK+aX&&aN>=Math.min(aZ,aJ)&&aN<=Math.max(aZ,aJ))){aY=[aW,aU/aT]}}}}if(aY){aW=aY[0];aU=aY[1];aT=Q[aW].datapoints.pointsize;return{datapoint:Q[aW].datapoints.points.slice(aU*aT,(aU+1)*aT),dataIndex:aU,series:Q[aW],seriesIndex:aW}}return null}function aa(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return aC.hoverable!=false})}}function l(aB){if(O.grid.hoverable){u("plothover",aB,function(aC){return false})}}function R(aB){u("plotclick",aB,function(aC){return aC.clickable!=false})}function u(aC,aB,aD){var aE=y.offset(),aH=aB.pageX-aE.left-q.left,aF=aB.pageY-aE.top-q.top,aJ=C({left:aH,top:aF});aJ.pageX=aB.pageX;aJ.pageY=aB.pageY;var aK=K(aH,aF,aD);if(aK){aK.pageX=parseInt(aK.series.xaxis.p2c(aK.datapoint[0])+aE.left+q.left);aK.pageY=parseInt(aK.series.yaxis.p2c(aK.datapoint[1])+aE.top+q.top)}if(O.grid.autoHighlight){for(var aG=0;aG<ab.length;++aG){var aI=ab[aG];if(aI.auto==aC&&!(aK&&aI.series==aK.series&&aI.point[0]==aK.datapoint[0]&&aI.point[1]==aK.datapoint[1])){T(aI.series,aI.point)}}if(aK){x(aK.series,aK.datapoint,aC)}}av.trigger(aC,[aJ,aK])}function f(){if(!M){M=setTimeout(s,30)}}function s(){M=null;A.save();A.clearRect(0,0,G,I);A.translate(q.left,q.top);var aC,aB;for(aC=0;aC<ab.length;++aC){aB=ab[aC];if(aB.series.bars.show){v(aB.series,aB.point)}else{ay(aB.series,aB.point)}}A.restore();an(ak.drawOverlay,[A])}function x(aD,aB,aF){if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){var aE=aD.datapoints.pointsize;aB=aD.datapoints.points.slice(aE*aB,aE*(aB+1))}var aC=al(aD,aB);if(aC==-1){ab.push({series:aD,point:aB,auto:aF});f()}else{if(!aF){ab[aC].auto=false}}}function T(aD,aB){if(aD==null&&aB==null){ab=[];f()}if(typeof aD=="number"){aD=Q[aD]}if(typeof aB=="number"){aB=aD.data[aB]}var aC=al(aD,aB);if(aC!=-1){ab.splice(aC,1);f()}}function al(aD,aE){for(var aB=0;aB<ab.length;++aB){var aC=ab[aB];if(aC.series==aD&&aC.point[0]==aE[0]&&aC.point[1]==aE[1]){return aB}}return -1}function ay(aE,aD){var aC=aD[0],aI=aD[1],aH=aE.xaxis,aG=aE.yaxis;if(aC<aH.min||aC>aH.max||aI<aG.min||aI>aG.max){return}var aF=aE.points.radius+aE.points.lineWidth/2;A.lineWidth=aF;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aB=1.5*aF,aC=aH.p2c(aC),aI=aG.p2c(aI);A.beginPath();if(aE.points.symbol=="circle"){A.arc(aC,aI,aB,0,2*Math.PI,false)}else{aE.points.symbol(A,aC,aI,aB,false)}A.closePath();A.stroke()}function v(aE,aB){A.lineWidth=aE.bars.lineWidth;A.strokeStyle=c.color.parse(aE.color).scale("a",0.5).toString();var aD=c.color.parse(aE.color).scale("a",0.5).toString();var aC=aE.bars.align=="left"?0:-aE.bars.barWidth/2;E(aB[0],aB[1],aB[2]||0,aC,aC+aE.bars.barWidth,0,function(){return aD},aE.xaxis,aE.yaxis,A,aE.bars.horizontal,aE.bars.lineWidth)}function am(aJ,aB,aH,aC){if(typeof aJ=="string"){return aJ}else{var aI=H.createLinearGradient(0,aH,0,aB);for(var aE=0,aD=aJ.colors.length;aE<aD;++aE){var aF=aJ.colors[aE];if(typeof aF!="string"){var aG=c.color.parse(aC);if(aF.brightness!=null){aG=aG.scale("rgb",aF.brightness)}if(aF.opacity!=null){aG.a*=aF.opacity}aF=aG.toString()}aI.addColorStop(aE/(aD-1),aF)}return aI}}}c.plot=function(g,e,d){var f=new b(c(g),e,d,c.plot.plugins);return f};c.plot.version="0.7";c.plot.plugins=[];c.plot.formatDate=function(l,f,h){var o=function(d){d=""+d;return d.length==1?"0"+d:d};var e=[];var p=false,j=false;var n=l.getUTCHours();var k=n<12;if(h==null){h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]}if(f.search(/%p|%P/)!=-1){if(n>12){n=n-12}else{if(n==0){n=12}}}for(var g=0;g<f.length;++g){var m=f.charAt(g);if(p){switch(m){case"h":m=""+n;break;case"H":m=o(n);break;case"M":m=o(l.getUTCMinutes());break;case"S":m=o(l.getUTCSeconds());break;case"d":m=""+l.getUTCDate();break;case"m":m=""+(l.getUTCMonth()+1);break;case"y":m=""+l.getUTCFullYear();break;case"b":m=""+h[l.getUTCMonth()];break;case"p":m=(k)?("am"):("pm");break;case"P":m=(k)?("AM"):("PM");break;case"0":m="";j=true;break}if(m&&j){m=o(m);j=false}e.push(m);if(!j){p=false}}else{if(m=="%"){p=true}else{e.push(m)}}}return e.join("")};function a(e,d){return d*Math.floor(e/d)}})(jQuery); |
/* | |
Flot plugin for adding panning and zooming capabilities to a plot. | |
The default behaviour is double click and scrollwheel up/down to zoom | |
in, drag to pan. The plugin defines plot.zoom({ center }), | |
plot.zoomOut() and plot.pan(offset) so you easily can add custom | |
controls. It also fires a "plotpan" and "plotzoom" event when | |
something happens, useful for synchronizing plots. | |
Options: | |
zoom: { | |
interactive: false | |
trigger: "dblclick" // or "click" for single click | |
amount: 1.5 // 2 = 200% (zoom in), 0.5 = 50% (zoom out) | |
} | |
pan: { | |
interactive: false | |
cursor: "move" // CSS mouse cursor value used when dragging, e.g. "pointer" | |
frameRate: 20 | |
} | |
xaxis, yaxis, x2axis, y2axis: { | |
zoomRange: null // or [number, number] (min range, max range) or false | |
panRange: null // or [number, number] (min, max) or false | |
} | |
"interactive" enables the built-in drag/click behaviour. If you enable | |
interactive for pan, then you'll have a basic plot that supports | |
moving around; the same for zoom. | |
"amount" specifies the default amount to zoom in (so 1.5 = 150%) | |
relative to the current viewport. | |
"cursor" is a standard CSS mouse cursor string used for visual | |
feedback to the user when dragging. | |
"frameRate" specifies the maximum number of times per second the plot | |
will update itself while the user is panning around on it (set to null | |
to disable intermediate pans, the plot will then not update until the | |
mouse button is released). | |
"zoomRange" is the interval in which zooming can happen, e.g. with | |
zoomRange: [1, 100] the zoom will never scale the axis so that the | |
difference between min and max is smaller than 1 or larger than 100. | |
You can set either end to null to ignore, e.g. [1, null]. If you set | |
zoomRange to false, zooming on that axis will be disabled. | |
"panRange" confines the panning to stay within a range, e.g. with | |
panRange: [-10, 20] panning stops at -10 in one end and at 20 in the | |
other. Either can be null, e.g. [-10, null]. If you set | |
panRange to false, panning on that axis will be disabled. | |
Example API usage: | |
plot = $.plot(...); | |
// zoom default amount in on the pixel (10, 20) | |
plot.zoom({ center: { left: 10, top: 20 } }); | |
// zoom out again | |
plot.zoomOut({ center: { left: 10, top: 20 } }); | |
// zoom 200% in on the pixel (10, 20) | |
plot.zoom({ amount: 2, center: { left: 10, top: 20 } }); | |
// pan 100 pixels to the left and 20 down | |
plot.pan({ left: -100, top: 20 }) | |
Here, "center" specifies where the center of the zooming should | |
happen. Note that this is defined in pixel space, not the space of the | |
data points (you can use the p2c helpers on the axes in Flot to help | |
you convert between these). | |
"amount" is the amount to zoom the viewport relative to the current | |
range, so 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is | |
70% (zoom out). You can set the default in the options. | |
*/ | |
// First two dependencies, jquery.event.drag.js and | |
// jquery.mousewheel.js, we put them inline here to save people the | |
// effort of downloading them. | |
/* | |
jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com) | |
Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt | |
*/ | |
(function(E){E.fn.drag=function(L,K,J){if(K){this.bind("dragstart",L)}if(J){this.bind("dragend",J)}return !L?this.trigger("drag"):this.bind("drag",K?K:L)};var A=E.event,B=A.special,F=B.drag={not:":input",distance:0,which:1,dragging:false,setup:function(J){J=E.extend({distance:F.distance,which:F.which,not:F.not},J||{});J.distance=I(J.distance);A.add(this,"mousedown",H,J);if(this.attachEvent){this.attachEvent("ondragstart",D)}},teardown:function(){A.remove(this,"mousedown",H);if(this===F.dragging){F.dragging=F.proxy=false}G(this,true);if(this.detachEvent){this.detachEvent("ondragstart",D)}}};B.dragstart=B.dragend={setup:function(){},teardown:function(){}};function H(L){var K=this,J,M=L.data||{};if(M.elem){K=L.dragTarget=M.elem;L.dragProxy=F.proxy||K;L.cursorOffsetX=M.pageX-M.left;L.cursorOffsetY=M.pageY-M.top;L.offsetX=L.pageX-L.cursorOffsetX;L.offsetY=L.pageY-L.cursorOffsetY}else{if(F.dragging||(M.which>0&&L.which!=M.which)||E(L.target).is(M.not)){return }}switch(L.type){case"mousedown":E.extend(M,E(K).offset(),{elem:K,target:L.target,pageX:L.pageX,pageY:L.pageY});A.add(document,"mousemove mouseup",H,M);G(K,false);F.dragging=null;return false;case !F.dragging&&"mousemove":if(I(L.pageX-M.pageX)+I(L.pageY-M.pageY)<M.distance){break}L.target=M.target;J=C(L,"dragstart",K);if(J!==false){F.dragging=K;F.proxy=L.dragProxy=E(J||K)[0]}case"mousemove":if(F.dragging){J=C(L,"drag",K);if(B.drop){B.drop.allowed=(J!==false);B.drop.handler(L)}if(J!==false){break}L.type="mouseup"}case"mouseup":A.remove(document,"mousemove mouseup",H);if(F.dragging){if(B.drop){B.drop.handler(L)}C(L,"dragend",K)}G(K,true);F.dragging=F.proxy=M.elem=false;break}return true}function C(M,K,L){M.type=K;var J=E.event.handle.call(L,M);return J===false?false:J||M.result}function I(J){return Math.pow(J,2)}function D(){return(F.dragging===false)}function G(K,J){if(!K){return }K.unselectable=J?"off":"on";K.onselectstart=function(){return J};if(K.style){K.style.MozUserSelect=J?"":"none"}}})(jQuery); | |
/* jquery.mousewheel.min.js | |
* Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net) | |
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) | |
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. | |
* Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers. | |
* Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix. | |
* | |
* Version: 3.0.2 | |
* | |
* Requires: 1.2.2+ | |
*/ | |
(function(c){var a=["DOMMouseScroll","mousewheel"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind("mousewheel",d):this.trigger("mousewheel")},unmousewheel:function(d){return this.unbind("mousewheel",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type="mousewheel";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery); | |
(function ($) { | |
var options = { | |
xaxis: { | |
zoomRange: null, // or [number, number] (min range, max range) | |
panRange: null // or [number, number] (min, max) | |
}, | |
zoom: { | |
interactive: false, | |
trigger: "dblclick", // or "click" for single click | |
amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out) | |
}, | |
pan: { | |
interactive: false, | |
cursor: "move", | |
frameRate: 20 | |
} | |
}; | |
function init(plot) { | |
function onZoomClick(e, zoomOut) { | |
var c = plot.offset(); | |
c.left = e.pageX - c.left; | |
c.top = e.pageY - c.top; | |
if (zoomOut) | |
plot.zoomOut({ center: c }); | |
else | |
plot.zoom({ center: c }); | |
} | |
function onMouseWheel(e, delta) { | |
onZoomClick(e, delta < 0); | |
return false; | |
} | |
var prevCursor = 'default', prevPageX = 0, prevPageY = 0, | |
panTimeout = null; | |
function onDragStart(e) { | |
if (e.which != 1) // only accept left-click | |
return false; | |
var c = plot.getPlaceholder().css('cursor'); | |
if (c) | |
prevCursor = c; | |
plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor); | |
prevPageX = e.pageX; | |
prevPageY = e.pageY; | |
} | |
function onDrag(e) { | |
var frameRate = plot.getOptions().pan.frameRate; | |
if (panTimeout || !frameRate) | |
return; | |
panTimeout = setTimeout(function () { | |
plot.pan({ left: prevPageX - e.pageX, | |
top: prevPageY - e.pageY }); | |
prevPageX = e.pageX; | |
prevPageY = e.pageY; | |
panTimeout = null; | |
}, 1 / frameRate * 1000); | |
} | |
function onDragEnd(e) { | |
if (panTimeout) { | |
clearTimeout(panTimeout); | |
panTimeout = null; | |
} | |
plot.getPlaceholder().css('cursor', prevCursor); | |
plot.pan({ left: prevPageX - e.pageX, | |
top: prevPageY - e.pageY }); | |
} | |
function bindEvents(plot, eventHolder) { | |
var o = plot.getOptions(); | |
if (o.zoom.interactive) { | |
eventHolder[o.zoom.trigger](onZoomClick); | |
eventHolder.mousewheel(onMouseWheel); | |
} | |
if (o.pan.interactive) { | |
eventHolder.bind("dragstart", { distance: 10 }, onDragStart); | |
eventHolder.bind("drag", onDrag); | |
eventHolder.bind("dragend", onDragEnd); | |
} | |
} | |
plot.zoomOut = function (args) { | |
if (!args) | |
args = {}; | |
if (!args.amount) | |
args.amount = plot.getOptions().zoom.amount | |
args.amount = 1 / args.amount; | |
plot.zoom(args); | |
} | |
plot.zoom = function (args) { | |
if (!args) | |
args = {}; | |
var c = args.center, | |
amount = args.amount || plot.getOptions().zoom.amount, | |
w = plot.width(), h = plot.height(); | |
if (!c) | |
c = { left: w / 2, top: h / 2 }; | |
var xf = c.left / w, | |
yf = c.top / h, | |
minmax = { | |
x: { | |
min: c.left - xf * w / amount, | |
max: c.left + (1 - xf) * w / amount | |
}, | |
y: { | |
min: c.top - yf * h / amount, | |
max: c.top + (1 - yf) * h / amount | |
} | |
}; | |
$.each(plot.getAxes(), function(_, axis) { | |
var opts = axis.options, | |
min = minmax[axis.direction].min, | |
max = minmax[axis.direction].max, | |
zr = opts.zoomRange; | |
if (zr === false) // no zooming on this axis | |
return; | |
min = axis.c2p(min); | |
max = axis.c2p(max); | |
if (min > max) { | |
// make sure min < max | |
var tmp = min; | |
min = max; | |
max = tmp; | |
} | |
var range = max - min; | |
if (zr && | |
((zr[0] != null && range < zr[0]) || | |
(zr[1] != null && range > zr[1]))) | |
return; | |
opts.min = min; | |
opts.max = max; | |
}); | |
plot.setupGrid(); | |
plot.draw(); | |
if (!args.preventEvent) | |
plot.getPlaceholder().trigger("plotzoom", [ plot ]); | |
} | |
plot.pan = function (args) { | |
var delta = { | |
x: +args.left, | |
y: +args.top | |
}; | |
if (isNaN(delta.x)) | |
delta.x = 0; | |
if (isNaN(delta.y)) | |
delta.y = 0; | |
$.each(plot.getAxes(), function (_, axis) { | |
var opts = axis.options, | |
min, max, d = delta[axis.direction]; | |
min = axis.c2p(axis.p2c(axis.min) + d), | |
max = axis.c2p(axis.p2c(axis.max) + d); | |
var pr = opts.panRange; | |
if (pr === false) // no panning on this axis | |
return; | |
if (pr) { | |
// check whether we hit the wall | |
if (pr[0] != null && pr[0] > min) { | |
d = pr[0] - min; | |
min += d; | |
max += d; | |
} | |
if (pr[1] != null && pr[1] < max) { | |
d = pr[1] - max; | |
min += d; | |
max += d; | |
} | |
} | |
opts.min = min; | |
opts.max = max; | |
}); | |
plot.setupGrid(); | |
plot.draw(); | |
if (!args.preventEvent) | |
plot.getPlaceholder().trigger("plotpan", [ plot ]); | |
} | |
function shutdown(plot, eventHolder) { | |
eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); | |
eventHolder.unbind("mousewheel", onMouseWheel); | |
eventHolder.unbind("dragstart", onDragStart); | |
eventHolder.unbind("drag", onDrag); | |
eventHolder.unbind("dragend", onDragEnd); | |
if (panTimeout) | |
clearTimeout(panTimeout); | |
} | |
plot.hooks.bindEvents.push(bindEvents); | |
plot.hooks.shutdown.push(shutdown); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'navigate', | |
version: '1.3' | |
}); | |
})(jQuery); | |
(function(i){i.fn.drag=function(j,k,l){if(k){this.bind("dragstart",j)}if(l){this.bind("dragend",l)}return !j?this.trigger("drag"):this.bind("drag",k?k:j)};var d=i.event,c=d.special,h=c.drag={not:":input",distance:0,which:1,dragging:false,setup:function(j){j=i.extend({distance:h.distance,which:h.which,not:h.not},j||{});j.distance=e(j.distance);d.add(this,"mousedown",f,j);if(this.attachEvent){this.attachEvent("ondragstart",a)}},teardown:function(){d.remove(this,"mousedown",f);if(this===h.dragging){h.dragging=h.proxy=false}g(this,true);if(this.detachEvent){this.detachEvent("ondragstart",a)}}};c.dragstart=c.dragend={setup:function(){},teardown:function(){}};function f(j){var k=this,l,m=j.data||{};if(m.elem){k=j.dragTarget=m.elem;j.dragProxy=h.proxy||k;j.cursorOffsetX=m.pageX-m.left;j.cursorOffsetY=m.pageY-m.top;j.offsetX=j.pageX-j.cursorOffsetX;j.offsetY=j.pageY-j.cursorOffsetY}else{if(h.dragging||(m.which>0&&j.which!=m.which)||i(j.target).is(m.not)){return}}switch(j.type){case"mousedown":i.extend(m,i(k).offset(),{elem:k,target:j.target,pageX:j.pageX,pageY:j.pageY});d.add(document,"mousemove mouseup",f,m);g(k,false);h.dragging=null;return false;case !h.dragging&&"mousemove":if(e(j.pageX-m.pageX)+e(j.pageY-m.pageY)<m.distance){break}j.target=m.target;l=b(j,"dragstart",k);if(l!==false){h.dragging=k;h.proxy=j.dragProxy=i(l||k)[0]}case"mousemove":if(h.dragging){l=b(j,"drag",k);if(c.drop){c.drop.allowed=(l!==false);c.drop.handler(j)}if(l!==false){break}j.type="mouseup"}case"mouseup":d.remove(document,"mousemove mouseup",f);if(h.dragging){if(c.drop){c.drop.handler(j)}b(j,"dragend",k)}g(k,true);h.dragging=h.proxy=m.elem=false;break}return true}function b(m,k,j){m.type=k;var l=i.event.handle.call(j,m);return l===false?false:l||m.result}function e(j){return Math.pow(j,2)}function a(){return(h.dragging===false)}function g(j,k){if(!j){return}j.unselectable=k?"off":"on";j.onselectstart=function(){return k};if(j.style){j.style.MozUserSelect=k?"":"none"}}})(jQuery);(function(f){var e=["DOMMouseScroll","mousewheel"];f.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var a=e.length;a;){this.addEventListener(e[--a],d,false)}}else{this.onmousewheel=d}},teardown:function(){if(this.removeEventListener){for(var a=e.length;a;){this.removeEventListener(e[--a],d,false)}}else{this.onmousewheel=null}}};f.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}});function d(b){var h=[].slice.call(arguments,1),a=0,c=true;b=f.event.fix(b||window.event);b.type="mousewheel";if(b.wheelDelta){a=b.wheelDelta/120}if(b.detail){a=-b.detail/3}h.unshift(b,a);return f.event.handle.apply(this,h)}})(jQuery);(function(b){var a={xaxis:{zoomRange:null,panRange:null},zoom:{interactive:false,trigger:"dblclick",amount:1.5},pan:{interactive:false,cursor:"move",frameRate:20}};function c(o){function m(q,p){var r=o.offset();r.left=q.pageX-r.left;r.top=q.pageY-r.top;if(p){o.zoomOut({center:r})}else{o.zoom({center:r})}}function d(p,q){m(p,q<0);return false}var i="default",g=0,e=0,n=null;function f(p){if(p.which!=1){return false}var q=o.getPlaceholder().css("cursor");if(q){i=q}o.getPlaceholder().css("cursor",o.getOptions().pan.cursor);g=p.pageX;e=p.pageY}function j(q){var p=o.getOptions().pan.frameRate;if(n||!p){return}n=setTimeout(function(){o.pan({left:g-q.pageX,top:e-q.pageY});g=q.pageX;e=q.pageY;n=null},1/p*1000)}function h(p){if(n){clearTimeout(n);n=null}o.getPlaceholder().css("cursor",i);o.pan({left:g-p.pageX,top:e-p.pageY})}function l(q,p){var r=q.getOptions();if(r.zoom.interactive){p[r.zoom.trigger](m);p.mousewheel(d)}if(r.pan.interactive){p.bind("dragstart",{distance:10},f);p.bind("drag",j);p.bind("dragend",h)}}o.zoomOut=function(p){if(!p){p={}}if(!p.amount){p.amount=o.getOptions().zoom.amount}p.amount=1/p.amount;o.zoom(p)};o.zoom=function(q){if(!q){q={}}var x=q.center,r=q.amount||o.getOptions().zoom.amount,p=o.width(),t=o.height();if(!x){x={left:p/2,top:t/2}}var s=x.left/p,v=x.top/t,u={x:{min:x.left-s*p/r,max:x.left+(1-s)*p/r},y:{min:x.top-v*t/r,max:x.top+(1-v)*t/r}};b.each(o.getAxes(),function(z,C){var D=C.options,B=u[C.direction].min,w=u[C.direction].max,E=D.zoomRange;if(E===false){return}B=C.c2p(B);w=C.c2p(w);if(B>w){var A=B;B=w;w=A}var y=w-B;if(E&&((E[0]!=null&&y<E[0])||(E[1]!=null&&y>E[1]))){return}D.min=B;D.max=w});o.setupGrid();o.draw();if(!q.preventEvent){o.getPlaceholder().trigger("plotzoom",[o])}};o.pan=function(p){var q={x:+p.left,y:+p.top};if(isNaN(q.x)){q.x=0}if(isNaN(q.y)){q.y=0}b.each(o.getAxes(),function(s,u){var v=u.options,t,r,w=q[u.direction];t=u.c2p(u.p2c(u.min)+w),r=u.c2p(u.p2c(u.max)+w);var x=v.panRange;if(x===false){return}if(x){if(x[0]!=null&&x[0]>t){w=x[0]-t;t+=w;r+=w}if(x[1]!=null&&x[1]<r){w=x[1]-r;t+=w;r+=w}}v.min=t;v.max=r});o.setupGrid();o.draw();if(!p.preventEvent){o.getPlaceholder().trigger("plotpan",[o])}};function k(q,p){p.unbind(q.getOptions().zoom.trigger,m);p.unbind("mousewheel",d);p.unbind("dragstart",f);p.unbind("drag",j);p.unbind("dragend",h);if(n){clearTimeout(n)}}o.hooks.bindEvents.push(l);o.hooks.shutdown.push(k)}b.plot.plugins.push({init:c,options:a,name:"navigate",version:"1.3"})})(jQuery); |
/* | |
Flot plugin for rendering pie charts. The plugin assumes the data is | |
coming is as a single data value for each series, and each of those | |
values is a positive value or zero (negative numbers don't make | |
any sense and will cause strange effects). The data values do | |
NOT need to be passed in as percentage values because it | |
internally calculates the total and percentages. | |
* Created by Brian Medendorp, June 2009 | |
* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars | |
* Changes: | |
2009-10-22: lineJoin set to round | |
2009-10-23: IE full circle fix, donut | |
2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera | |
2009-11-17: Added IE hover capability submitted by Anthony Aragues | |
2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well) | |
Available options are: | |
series: { | |
pie: { | |
show: true/false | |
radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto' | |
innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect | |
startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result | |
tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show) | |
offset: { | |
top: integer value to move the pie up or down | |
left: integer value to move the pie left or right, or 'auto' | |
}, | |
stroke: { | |
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF') | |
width: integer pixel width of the stroke | |
}, | |
label: { | |
show: true/false, or 'auto' | |
formatter: a user-defined function that modifies the text/style of the label text | |
radius: 0-1 for percentage of fullsize, or a specified pixel length | |
background: { | |
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000') | |
opacity: 0-1 | |
}, | |
threshold: 0-1 for the percentage value at which to hide labels (if they're too small) | |
}, | |
combine: { | |
threshold: 0-1 for the percentage value at which to combine slices (if they're too small) | |
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined | |
label: any text value of what the combined slice should be labeled | |
} | |
highlight: { | |
opacity: 0-1 | |
} | |
} | |
} | |
More detail and specific examples can be found in the included HTML file. | |
*/ | |
(function ($) | |
{ | |
function init(plot) // this is the "body" of the plugin | |
{ | |
var canvas = null; | |
var target = null; | |
var maxRadius = null; | |
var centerLeft = null; | |
var centerTop = null; | |
var total = 0; | |
var redraw = true; | |
var redrawAttempts = 10; | |
var shrink = 0.95; | |
var legendWidth = 0; | |
var processed = false; | |
var raw = false; | |
// interactive variables | |
var highlights = []; | |
// add hook to determine if pie plugin in enabled, and then perform necessary operations | |
plot.hooks.processOptions.push(checkPieEnabled); | |
plot.hooks.bindEvents.push(bindEvents); | |
// check to see if the pie plugin is enabled | |
function checkPieEnabled(plot, options) | |
{ | |
if (options.series.pie.show) | |
{ | |
//disable grid | |
options.grid.show = false; | |
// set labels.show | |
if (options.series.pie.label.show=='auto') | |
if (options.legend.show) | |
options.series.pie.label.show = false; | |
else | |
options.series.pie.label.show = true; | |
// set radius | |
if (options.series.pie.radius=='auto') | |
if (options.series.pie.label.show) | |
options.series.pie.radius = 3/4; | |
else | |
options.series.pie.radius = 1; | |
// ensure sane tilt | |
if (options.series.pie.tilt>1) | |
options.series.pie.tilt=1; | |
if (options.series.pie.tilt<0) | |
options.series.pie.tilt=0; | |
// add processData hook to do transformations on the data | |
plot.hooks.processDatapoints.push(processDatapoints); | |
plot.hooks.drawOverlay.push(drawOverlay); | |
// add draw hook | |
plot.hooks.draw.push(draw); | |
} | |
} | |
// bind hoverable events | |
function bindEvents(plot, eventHolder) | |
{ | |
var options = plot.getOptions(); | |
if (options.series.pie.show && options.grid.hoverable) | |
eventHolder.unbind('mousemove').mousemove(onMouseMove); | |
if (options.series.pie.show && options.grid.clickable) | |
eventHolder.unbind('click').click(onClick); | |
} | |
// debugging function that prints out an object | |
function alertObject(obj) | |
{ | |
var msg = ''; | |
function traverse(obj, depth) | |
{ | |
if (!depth) | |
depth = 0; | |
for (var i = 0; i < obj.length; ++i) | |
{ | |
for (var j=0; j<depth; j++) | |
msg += '\t'; | |
if( typeof obj[i] == "object") | |
{ // its an object | |
msg += ''+i+':\n'; | |
traverse(obj[i], depth+1); | |
} | |
else | |
{ // its a value | |
msg += ''+i+': '+obj[i]+'\n'; | |
} | |
} | |
} | |
traverse(obj); | |
alert(msg); | |
} | |
function calcTotal(data) | |
{ | |
for (var i = 0; i < data.length; ++i) | |
{ | |
var item = parseFloat(data[i].data[0][1]); | |
if (item) | |
total += item; | |
} | |
} | |
function processDatapoints(plot, series, data, datapoints) | |
{ | |
if (!processed) | |
{ | |
processed = true; | |
canvas = plot.getCanvas(); | |
target = $(canvas).parent(); | |
options = plot.getOptions(); | |
plot.setData(combine(plot.getData())); | |
} | |
} | |
function setupPie() | |
{ | |
legendWidth = target.children().filter('.legend').children().width(); | |
// calculate maximum radius and center point | |
maxRadius = Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2; | |
centerTop = (canvas.height/2)+options.series.pie.offset.top; | |
centerLeft = (canvas.width/2); | |
if (options.series.pie.offset.left=='auto') | |
if (options.legend.position.match('w')) | |
centerLeft += legendWidth/2; | |
else | |
centerLeft -= legendWidth/2; | |
else | |
centerLeft += options.series.pie.offset.left; | |
if (centerLeft<maxRadius) | |
centerLeft = maxRadius; | |
else if (centerLeft>canvas.width-maxRadius) | |
centerLeft = canvas.width-maxRadius; | |
} | |
function fixData(data) | |
{ | |
for (var i = 0; i < data.length; ++i) | |
{ | |
if (typeof(data[i].data)=='number') | |
data[i].data = [[1,data[i].data]]; | |
else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined') | |
{ | |
if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined') | |
data[i].label = data[i].data.label; // fix weirdness coming from flot | |
data[i].data = [[1,0]]; | |
} | |
} | |
return data; | |
} | |
function combine(data) | |
{ | |
data = fixData(data); | |
calcTotal(data); | |
var combined = 0; | |
var numCombined = 0; | |
var color = options.series.pie.combine.color; | |
var newdata = []; | |
for (var i = 0; i < data.length; ++i) | |
{ | |
// make sure its a number | |
data[i].data[0][1] = parseFloat(data[i].data[0][1]); | |
if (!data[i].data[0][1]) | |
data[i].data[0][1] = 0; | |
if (data[i].data[0][1]/total<=options.series.pie.combine.threshold) | |
{ | |
combined += data[i].data[0][1]; | |
numCombined++; | |
if (!color) | |
color = data[i].color; | |
} | |
else | |
{ | |
newdata.push({ | |
data: [[1,data[i].data[0][1]]], | |
color: data[i].color, | |
label: data[i].label, | |
angle: (data[i].data[0][1]*(Math.PI*2))/total, | |
percent: (data[i].data[0][1]/total*100) | |
}); | |
} | |
} | |
if (numCombined>0) | |
newdata.push({ | |
data: [[1,combined]], | |
color: color, | |
label: options.series.pie.combine.label, | |
angle: (combined*(Math.PI*2))/total, | |
percent: (combined/total*100) | |
}); | |
return newdata; | |
} | |
function draw(plot, newCtx) | |
{ | |
if (!target) return; // if no series were passed | |
ctx = newCtx; | |
setupPie(); | |
var slices = plot.getData(); | |
var attempts = 0; | |
while (redraw && attempts<redrawAttempts) | |
{ | |
redraw = false; | |
if (attempts>0) | |
maxRadius *= shrink; | |
attempts += 1; | |
clear(); | |
if (options.series.pie.tilt<=0.8) | |
drawShadow(); | |
drawPie(); | |
} | |
if (attempts >= redrawAttempts) { | |
clear(); | |
target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>'); | |
} | |
if ( plot.setSeries && plot.insertLegend ) | |
{ | |
plot.setSeries(slices); | |
plot.insertLegend(); | |
} | |
// we're actually done at this point, just defining internal functions at this point | |
function clear() | |
{ | |
ctx.clearRect(0,0,canvas.width,canvas.height); | |
target.children().filter('.pieLabel, .pieLabelBackground').remove(); | |
} | |
function drawShadow() | |
{ | |
var shadowLeft = 5; | |
var shadowTop = 15; | |
var edge = 10; | |
var alpha = 0.02; | |
// set radius | |
if (options.series.pie.radius>1) | |
var radius = options.series.pie.radius; | |
else | |
var radius = maxRadius * options.series.pie.radius; | |
if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge) | |
return; // shadow would be outside canvas, so don't draw it | |
ctx.save(); | |
ctx.translate(shadowLeft,shadowTop); | |
ctx.globalAlpha = alpha; | |
ctx.fillStyle = '#000'; | |
// center and rotate to starting position | |
ctx.translate(centerLeft,centerTop); | |
ctx.scale(1, options.series.pie.tilt); | |
//radius -= edge; | |
for (var i=1; i<=edge; i++) | |
{ | |
ctx.beginPath(); | |
ctx.arc(0,0,radius,0,Math.PI*2,false); | |
ctx.fill(); | |
radius -= i; | |
} | |
ctx.restore(); | |
} | |
function drawPie() | |
{ | |
startAngle = Math.PI*options.series.pie.startAngle; | |
// set radius | |
if (options.series.pie.radius>1) | |
var radius = options.series.pie.radius; | |
else | |
var radius = maxRadius * options.series.pie.radius; | |
// center and rotate to starting position | |
ctx.save(); | |
ctx.translate(centerLeft,centerTop); | |
ctx.scale(1, options.series.pie.tilt); | |
//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera | |
// draw slices | |
ctx.save(); | |
var currentAngle = startAngle; | |
for (var i = 0; i < slices.length; ++i) | |
{ | |
slices[i].startAngle = currentAngle; | |
drawSlice(slices[i].angle, slices[i].color, true); | |
} | |
ctx.restore(); | |
// draw slice outlines | |
ctx.save(); | |
ctx.lineWidth = options.series.pie.stroke.width; | |
currentAngle = startAngle; | |
for (var i = 0; i < slices.length; ++i) | |
drawSlice(slices[i].angle, options.series.pie.stroke.color, false); | |
ctx.restore(); | |
// draw donut hole | |
drawDonutHole(ctx); | |
// draw labels | |
if (options.series.pie.label.show) | |
drawLabels(); | |
// restore to original state | |
ctx.restore(); | |
function drawSlice(angle, color, fill) | |
{ | |
if (angle<=0) | |
return; | |
if (fill) | |
ctx.fillStyle = color; | |
else | |
{ | |
ctx.strokeStyle = color; | |
ctx.lineJoin = 'round'; | |
} | |
ctx.beginPath(); | |
if (Math.abs(angle - Math.PI*2) > 0.000000001) | |
ctx.moveTo(0,0); // Center of the pie | |
else if ($.browser.msie) | |
angle -= 0.0001; | |
//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera | |
ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false); | |
ctx.closePath(); | |
//ctx.rotate(angle); // This doesn't work properly in Opera | |
currentAngle += angle; | |
if (fill) | |
ctx.fill(); | |
else | |
ctx.stroke(); | |
} | |
function drawLabels() | |
{ | |
var currentAngle = startAngle; | |
// set radius | |
if (options.series.pie.label.radius>1) | |
var radius = options.series.pie.label.radius; | |
else | |
var radius = maxRadius * options.series.pie.label.radius; | |
for (var i = 0; i < slices.length; ++i) | |
{ | |
if (slices[i].percent >= options.series.pie.label.threshold*100) | |
drawLabel(slices[i], currentAngle, i); | |
currentAngle += slices[i].angle; | |
} | |
function drawLabel(slice, startAngle, index) | |
{ | |
if (slice.data[0][1]==0) | |
return; | |
// format label text | |
var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter; | |
if (lf) | |
text = lf(slice.label, slice); | |
else | |
text = slice.label; | |
if (plf) | |
text = plf(text, slice); | |
var halfAngle = ((startAngle+slice.angle) + startAngle)/2; | |
var x = centerLeft + Math.round(Math.cos(halfAngle) * radius); | |
var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt; | |
var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>"; | |
target.append(html); | |
var label = target.children('#pieLabel'+index); | |
var labelTop = (y - label.height()/2); | |
var labelLeft = (x - label.width()/2); | |
label.css('top', labelTop); | |
label.css('left', labelLeft); | |
// check to make sure that the label is not outside the canvas | |
if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0) | |
redraw = true; | |
if (options.series.pie.label.background.opacity != 0) { | |
// put in the transparent background separately to avoid blended labels and label boxes | |
var c = options.series.pie.label.background.color; | |
if (c == null) { | |
c = slice.color; | |
} | |
var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;'; | |
$('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity); | |
} | |
} // end individual label function | |
} // end drawLabels function | |
} // end drawPie function | |
} // end draw function | |
// Placed here because it needs to be accessed from multiple locations | |
function drawDonutHole(layer) | |
{ | |
// draw donut hole | |
if(options.series.pie.innerRadius > 0) | |
{ | |
// subtract the center | |
layer.save(); | |
innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius; | |
layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color | |
layer.beginPath(); | |
layer.fillStyle = options.series.pie.stroke.color; | |
layer.arc(0,0,innerRadius,0,Math.PI*2,false); | |
layer.fill(); | |
layer.closePath(); | |
layer.restore(); | |
// add inner stroke | |
layer.save(); | |
layer.beginPath(); | |
layer.strokeStyle = options.series.pie.stroke.color; | |
layer.arc(0,0,innerRadius,0,Math.PI*2,false); | |
layer.stroke(); | |
layer.closePath(); | |
layer.restore(); | |
// TODO: add extra shadow inside hole (with a mask) if the pie is tilted. | |
} | |
} | |
//-- Additional Interactive related functions -- | |
function isPointInPoly(poly, pt) | |
{ | |
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) | |
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1])) | |
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0]) | |
&& (c = !c); | |
return c; | |
} | |
function findNearbySlice(mouseX, mouseY) | |
{ | |
var slices = plot.getData(), | |
options = plot.getOptions(), | |
radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; | |
for (var i = 0; i < slices.length; ++i) | |
{ | |
var s = slices[i]; | |
if(s.pie.show) | |
{ | |
ctx.save(); | |
ctx.beginPath(); | |
ctx.moveTo(0,0); // Center of the pie | |
//ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here. | |
ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false); | |
ctx.closePath(); | |
x = mouseX-centerLeft; | |
y = mouseY-centerTop; | |
if(ctx.isPointInPath) | |
{ | |
if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop)) | |
{ | |
//alert('found slice!'); | |
ctx.restore(); | |
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; | |
} | |
} | |
else | |
{ | |
// excanvas for IE doesn;t support isPointInPath, this is a workaround. | |
p1X = (radius * Math.cos(s.startAngle)); | |
p1Y = (radius * Math.sin(s.startAngle)); | |
p2X = (radius * Math.cos(s.startAngle+(s.angle/4))); | |
p2Y = (radius * Math.sin(s.startAngle+(s.angle/4))); | |
p3X = (radius * Math.cos(s.startAngle+(s.angle/2))); | |
p3Y = (radius * Math.sin(s.startAngle+(s.angle/2))); | |
p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5))); | |
p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5))); | |
p5X = (radius * Math.cos(s.startAngle+s.angle)); | |
p5Y = (radius * Math.sin(s.startAngle+s.angle)); | |
arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]]; | |
arrPoint = [x,y]; | |
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt? | |
if(isPointInPoly(arrPoly, arrPoint)) | |
{ | |
ctx.restore(); | |
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i}; | |
} | |
} | |
ctx.restore(); | |
} | |
} | |
return null; | |
} | |
function onMouseMove(e) | |
{ | |
triggerClickHoverEvent('plothover', e); | |
} | |
function onClick(e) | |
{ | |
triggerClickHoverEvent('plotclick', e); | |
} | |
// trigger click or hover event (they send the same parameters so we share their code) | |
function triggerClickHoverEvent(eventname, e) | |
{ | |
var offset = plot.offset(), | |
canvasX = parseInt(e.pageX - offset.left), | |
canvasY = parseInt(e.pageY - offset.top), | |
item = findNearbySlice(canvasX, canvasY); | |
if (options.grid.autoHighlight) | |
{ | |
// clear auto-highlights | |
for (var i = 0; i < highlights.length; ++i) | |
{ | |
var h = highlights[i]; | |
if (h.auto == eventname && !(item && h.series == item.series)) | |
unhighlight(h.series); | |
} | |
} | |
// highlight the slice | |
if (item) | |
highlight(item.series, eventname); | |
// trigger any hover bind events | |
var pos = { pageX: e.pageX, pageY: e.pageY }; | |
target.trigger(eventname, [ pos, item ]); | |
} | |
function highlight(s, auto) | |
{ | |
if (typeof s == "number") | |
s = series[s]; | |
var i = indexOfHighlight(s); | |
if (i == -1) | |
{ | |
highlights.push({ series: s, auto: auto }); | |
plot.triggerRedrawOverlay(); | |
} | |
else if (!auto) | |
highlights[i].auto = false; | |
} | |
function unhighlight(s) | |
{ | |
if (s == null) | |
{ | |
highlights = []; | |
plot.triggerRedrawOverlay(); | |
} | |
if (typeof s == "number") | |
s = series[s]; | |
var i = indexOfHighlight(s); | |
if (i != -1) | |
{ | |
highlights.splice(i, 1); | |
plot.triggerRedrawOverlay(); | |
} | |
} | |
function indexOfHighlight(s) | |
{ | |
for (var i = 0; i < highlights.length; ++i) | |
{ | |
var h = highlights[i]; | |
if (h.series == s) | |
return i; | |
} | |
return -1; | |
} | |
function drawOverlay(plot, octx) | |
{ | |
//alert(options.series.pie.radius); | |
var options = plot.getOptions(); | |
//alert(options.series.pie.radius); | |
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius; | |
octx.save(); | |
octx.translate(centerLeft, centerTop); | |
octx.scale(1, options.series.pie.tilt); | |
for (i = 0; i < highlights.length; ++i) | |
drawHighlight(highlights[i].series); | |
drawDonutHole(octx); | |
octx.restore(); | |
function drawHighlight(series) | |
{ | |
if (series.angle < 0) return; | |
//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString(); | |
octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor | |
octx.beginPath(); | |
if (Math.abs(series.angle - Math.PI*2) > 0.000000001) | |
octx.moveTo(0,0); // Center of the pie | |
octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false); | |
octx.closePath(); | |
octx.fill(); | |
} | |
} | |
} // end init (plugin body) | |
// define pie specific options and their default values | |
var options = { | |
series: { | |
pie: { | |
show: false, | |
radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value) | |
innerRadius:0, /* for donut */ | |
startAngle: 3/2, | |
tilt: 1, | |
offset: { | |
top: 0, | |
left: 'auto' | |
}, | |
stroke: { | |
color: '#FFF', | |
width: 1 | |
}, | |
label: { | |
show: 'auto', | |
formatter: function(label, slice){ | |
return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>'; | |
}, // formatter function | |
radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value) | |
background: { | |
color: null, | |
opacity: 0 | |
}, | |
threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow) | |
}, | |
combine: { | |
threshold: -1, // percentage at which to combine little slices into one larger slice | |
color: null, // color to give the new slice (auto-generated if null) | |
label: 'Other' // label to give the new slice | |
}, | |
highlight: { | |
//color: '#FFF', // will add this functionality once parseColor is available | |
opacity: 0.5 | |
} | |
} | |
} | |
}; | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: "pie", | |
version: "1.0" | |
}); | |
})(jQuery); | |
(function(b){function c(D){var h=null;var L=null;var n=null;var B=null;var p=null;var M=0;var F=true;var o=10;var w=0.95;var A=0;var d=false;var z=false;var j=[];D.hooks.processOptions.push(g);D.hooks.bindEvents.push(e);function g(O,N){if(N.series.pie.show){N.grid.show=false;if(N.series.pie.label.show=="auto"){if(N.legend.show){N.series.pie.label.show=false}else{N.series.pie.label.show=true}}if(N.series.pie.radius=="auto"){if(N.series.pie.label.show){N.series.pie.radius=3/4}else{N.series.pie.radius=1}}if(N.series.pie.tilt>1){N.series.pie.tilt=1}if(N.series.pie.tilt<0){N.series.pie.tilt=0}O.hooks.processDatapoints.push(E);O.hooks.drawOverlay.push(H);O.hooks.draw.push(r)}}function e(P,N){var O=P.getOptions();if(O.series.pie.show&&O.grid.hoverable){N.unbind("mousemove").mousemove(t)}if(O.series.pie.show&&O.grid.clickable){N.unbind("click").click(l)}}function G(O){var P="";function N(S,T){if(!T){T=0}for(var R=0;R<S.length;++R){for(var Q=0;Q<T;Q++){P+="\t"}if(typeof S[R]=="object"){P+=""+R+":\n";N(S[R],T+1)}else{P+=""+R+": "+S[R]+"\n"}}}N(O);alert(P)}function q(P){for(var N=0;N<P.length;++N){var O=parseFloat(P[N].data[0][1]);if(O){M+=O}}}function E(Q,N,O,P){if(!d){d=true;h=Q.getCanvas();L=b(h).parent();a=Q.getOptions();Q.setData(K(Q.getData()))}}function I(){A=L.children().filter(".legend").children().width();n=Math.min(h.width,(h.height/a.series.pie.tilt))/2;p=(h.height/2)+a.series.pie.offset.top;B=(h.width/2);if(a.series.pie.offset.left=="auto"){if(a.legend.position.match("w")){B+=A/2}else{B-=A/2}}else{B+=a.series.pie.offset.left}if(B<n){B=n}else{if(B>h.width-n){B=h.width-n}}}function v(O){for(var N=0;N<O.length;++N){if(typeof(O[N].data)=="number"){O[N].data=[[1,O[N].data]]}else{if(typeof(O[N].data)=="undefined"||typeof(O[N].data[0])=="undefined"){if(typeof(O[N].data)!="undefined"&&typeof(O[N].data.label)!="undefined"){O[N].label=O[N].data.label}O[N].data=[[1,0]]}}}return O}function K(Q){Q=v(Q);q(Q);var P=0;var S=0;var N=a.series.pie.combine.color;var R=[];for(var O=0;O<Q.length;++O){Q[O].data[0][1]=parseFloat(Q[O].data[0][1]);if(!Q[O].data[0][1]){Q[O].data[0][1]=0}if(Q[O].data[0][1]/M<=a.series.pie.combine.threshold){P+=Q[O].data[0][1];S++;if(!N){N=Q[O].color}}else{R.push({data:[[1,Q[O].data[0][1]]],color:Q[O].color,label:Q[O].label,angle:(Q[O].data[0][1]*(Math.PI*2))/M,percent:(Q[O].data[0][1]/M*100)})}}if(S>0){R.push({data:[[1,P]],color:N,label:a.series.pie.combine.label,angle:(P*(Math.PI*2))/M,percent:(P/M*100)})}return R}function r(S,Q){if(!L){return}ctx=Q;I();var T=S.getData();var P=0;while(F&&P<o){F=false;if(P>0){n*=w}P+=1;N();if(a.series.pie.tilt<=0.8){O()}R()}if(P>=o){N();L.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>')}if(S.setSeries&&S.insertLegend){S.setSeries(T);S.insertLegend()}function N(){ctx.clearRect(0,0,h.width,h.height);L.children().filter(".pieLabel, .pieLabelBackground").remove()}function O(){var Z=5;var Y=15;var W=10;var X=0.02;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}if(U>=(h.width/2)-Z||U*a.series.pie.tilt>=(h.height/2)-Y||U<=W){return}ctx.save();ctx.translate(Z,Y);ctx.globalAlpha=X;ctx.fillStyle="#000";ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);for(var V=1;V<=W;V++){ctx.beginPath();ctx.arc(0,0,U,0,Math.PI*2,false);ctx.fill();U-=V}ctx.restore()}function R(){startAngle=Math.PI*a.series.pie.startAngle;if(a.series.pie.radius>1){var U=a.series.pie.radius}else{var U=n*a.series.pie.radius}ctx.save();ctx.translate(B,p);ctx.scale(1,a.series.pie.tilt);ctx.save();var Y=startAngle;for(var W=0;W<T.length;++W){T[W].startAngle=Y;X(T[W].angle,T[W].color,true)}ctx.restore();ctx.save();ctx.lineWidth=a.series.pie.stroke.width;Y=startAngle;for(var W=0;W<T.length;++W){X(T[W].angle,a.series.pie.stroke.color,false)}ctx.restore();J(ctx);if(a.series.pie.label.show){V()}ctx.restore();function X(ab,Z,aa){if(ab<=0){return}if(aa){ctx.fillStyle=Z}else{ctx.strokeStyle=Z;ctx.lineJoin="round"}ctx.beginPath();if(Math.abs(ab-Math.PI*2)>1e-9){ctx.moveTo(0,0)}else{if(b.browser.msie){ab-=0.0001}}ctx.arc(0,0,U,Y,Y+ab,false);ctx.closePath();Y+=ab;if(aa){ctx.fill()}else{ctx.stroke()}}function V(){var ac=startAngle;if(a.series.pie.label.radius>1){var Z=a.series.pie.label.radius}else{var Z=n*a.series.pie.label.radius}for(var ab=0;ab<T.length;++ab){if(T[ab].percent>=a.series.pie.label.threshold*100){aa(T[ab],ac,ab)}ac+=T[ab].angle}function aa(ap,ai,ag){if(ap.data[0][1]==0){return}var ar=a.legend.labelFormatter,aq,ae=a.series.pie.label.formatter;if(ar){aq=ar(ap.label,ap)}else{aq=ap.label}if(ae){aq=ae(aq,ap)}var aj=((ai+ap.angle)+ai)/2;var ao=B+Math.round(Math.cos(aj)*Z);var am=p+Math.round(Math.sin(aj)*Z)*a.series.pie.tilt;var af='<span class="pieLabel" id="pieLabel'+ag+'" style="position:absolute;top:'+am+"px;left:"+ao+'px;">'+aq+"</span>";L.append(af);var an=L.children("#pieLabel"+ag);var ad=(am-an.height()/2);var ah=(ao-an.width()/2);an.css("top",ad);an.css("left",ah);if(0-ad>0||0-ah>0||h.height-(ad+an.height())<0||h.width-(ah+an.width())<0){F=true}if(a.series.pie.label.background.opacity!=0){var ak=a.series.pie.label.background.color;if(ak==null){ak=ap.color}var al="top:"+ad+"px;left:"+ah+"px;";b('<div class="pieLabelBackground" style="position:absolute;width:'+an.width()+"px;height:"+an.height()+"px;"+al+"background-color:"+ak+';"> </div>').insertBefore(an).css("opacity",a.series.pie.label.background.opacity)}}}}}function J(N){if(a.series.pie.innerRadius>0){N.save();innerRadius=a.series.pie.innerRadius>1?a.series.pie.innerRadius:n*a.series.pie.innerRadius;N.globalCompositeOperation="destination-out";N.beginPath();N.fillStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.fill();N.closePath();N.restore();N.save();N.beginPath();N.strokeStyle=a.series.pie.stroke.color;N.arc(0,0,innerRadius,0,Math.PI*2,false);N.stroke();N.closePath();N.restore()}}function s(Q,R){for(var S=false,P=-1,N=Q.length,O=N-1;++P<N;O=P){((Q[P][1]<=R[1]&&R[1]<Q[O][1])||(Q[O][1]<=R[1]&&R[1]<Q[P][1]))&&(R[0]<(Q[O][0]-Q[P][0])*(R[1]-Q[P][1])/(Q[O][1]-Q[P][1])+Q[P][0])&&(S=!S)}return S}function u(R,P){var T=D.getData(),O=D.getOptions(),N=O.series.pie.radius>1?O.series.pie.radius:n*O.series.pie.radius;for(var Q=0;Q<T.length;++Q){var S=T[Q];if(S.pie.show){ctx.save();ctx.beginPath();ctx.moveTo(0,0);ctx.arc(0,0,N,S.startAngle,S.startAngle+S.angle,false);ctx.closePath();x=R-B;y=P-p;if(ctx.isPointInPath){if(ctx.isPointInPath(R-B,P-p)){ctx.restore();return{datapoint:[S.percent,S.data],dataIndex:0,series:S,seriesIndex:Q}}}else{p1X=(N*Math.cos(S.startAngle));p1Y=(N*Math.sin(S.startAngle));p2X=(N*Math.cos(S.startAngle+(S.angle/4)));p2Y=(N*Math.sin(S.startAngle+(S.angle/4)));p3X=(N*Math.cos(S.startAngle+(S.angle/2)));p3Y=(N*Math.sin(S.startAngle+(S.angle/2)));p4X=(N*Math.cos(S.startAngle+(S.angle/1.5)));p4Y=(N*Math.sin(S.startAngle+(S.angle/1.5)));p5X=(N*Math.cos(S.startAngle+S.angle));p5Y=(N*Math.sin(S.startAngle+S.angle));arrPoly=[[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];arrPoint=[x,y];if(s(arrPoly,arrPoint)){ctx.restore();return{datapoint:[S.percent,S.data],dataIndex:0,series:S,seriesIndex:Q}}}ctx.restore()}}return null}function t(N){m("plothover",N)}function l(N){m("plotclick",N)}function m(N,T){var O=D.offset(),R=parseInt(T.pageX-O.left),P=parseInt(T.pageY-O.top),V=u(R,P);if(a.grid.autoHighlight){for(var Q=0;Q<j.length;++Q){var S=j[Q];if(S.auto==N&&!(V&&S.series==V.series)){f(S.series)}}}if(V){k(V.series,N)}var U={pageX:T.pageX,pageY:T.pageY};L.trigger(N,[U,V])}function k(O,P){if(typeof O=="number"){O=series[O]}var N=C(O);if(N==-1){j.push({series:O,auto:P});D.triggerRedrawOverlay()}else{if(!P){j[N].auto=false}}}function f(O){if(O==null){j=[];D.triggerRedrawOverlay()}if(typeof O=="number"){O=series[O]}var N=C(O);if(N!=-1){j.splice(N,1);D.triggerRedrawOverlay()}}function C(P){for(var N=0;N<j.length;++N){var O=j[N];if(O.series==P){return N}}return -1}function H(Q,R){var P=Q.getOptions();var N=P.series.pie.radius>1?P.series.pie.radius:n*P.series.pie.radius;R.save();R.translate(B,p);R.scale(1,P.series.pie.tilt);for(i=0;i<j.length;++i){O(j[i].series)}J(R);R.restore();function O(S){if(S.angle<0){return}R.fillStyle="rgba(255, 255, 255, "+P.series.pie.highlight.opacity+")";R.beginPath();if(Math.abs(S.angle-Math.PI*2)>1e-9){R.moveTo(0,0)}R.arc(0,0,N,S.startAngle,S.startAngle+S.angle,false);R.closePath();R.fill()}}}var a={series:{pie:{show:false,radius:"auto",innerRadius:0,startAngle:3/2,tilt:1,offset:{top:0,left:"auto"},stroke:{color:"#FFF",width:1},label:{show:"auto",formatter:function(d,e){return'<div style="font-size:x-small;text-align:center;padding:2px;color:'+e.color+';">'+d+"<br/>"+Math.round(e.percent)+"%</div>"},radius:1,background:{color:null,opacity:0},threshold:0},combine:{threshold:-1,color:null,label:"Other"},highlight:{opacity:0.5}}}};b.plot.plugins.push({init:c,options:a,name:"pie",version:"1.0"})})(jQuery); |
/* | |
Flot plugin for automatically redrawing plots when the placeholder | |
size changes, e.g. on window resizes. | |
It works by listening for changes on the placeholder div (through the | |
jQuery resize event plugin) - if the size changes, it will redraw the | |
plot. | |
There are no options. If you need to disable the plugin for some | |
plots, you can just fix the size of their placeholders. | |
*/ | |
/* Inline dependency: | |
* jQuery resize event - v1.1 - 3/14/2010 | |
* http://benalman.com/projects/jquery-resize-plugin/ | |
* | |
* Copyright (c) 2010 "Cowboy" Ben Alman | |
* Dual licensed under the MIT and GPL licenses. | |
* http://benalman.com/about/license/ | |
*/ | |
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this); | |
(function ($) { | |
var options = { }; // no options | |
function init(plot) { | |
function onResize() { | |
var placeholder = plot.getPlaceholder(); | |
// somebody might have hidden us and we can't plot | |
// when we don't have the dimensions | |
if (placeholder.width() == 0 || placeholder.height() == 0) | |
return; | |
plot.resize(); | |
plot.setupGrid(); | |
plot.draw(); | |
} | |
function bindEvents(plot, eventHolder) { | |
plot.getPlaceholder().resize(onResize); | |
} | |
function shutdown(plot, eventHolder) { | |
plot.getPlaceholder().unbind("resize", onResize); | |
} | |
plot.hooks.bindEvents.push(bindEvents); | |
plot.hooks.shutdown.push(shutdown); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'resize', | |
version: '1.0' | |
}); | |
})(jQuery); | |
(function(n,p,u){var w=n([]),s=n.resize=n.extend(n.resize,{}),o,l="setTimeout",m="resize",t=m+"-special-event",v="delay",r="throttleWindow";s[v]=250;s[r]=true;n.event.special[m]={setup:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.add(a);n.data(this,t,{w:a.width(),h:a.height()});if(w.length===1){q()}},teardown:function(){if(!s[r]&&this[l]){return false}var a=n(this);w=w.not(a);a.removeData(t);if(!w.length){clearTimeout(o)}},add:function(b){if(!s[r]&&this[l]){return false}var c;function a(d,h,g){var f=n(this),e=n.data(this,t);e.w=h!==u?h:f.width();e.h=g!==u?g:f.height();c.apply(this,arguments)}if(n.isFunction(b)){c=b;return a}else{c=b.handler;b.handler=a}}};function q(){o=p[l](function(){w.each(function(){var d=n(this),a=d.width(),b=d.height(),c=n.data(this,t);if(a!==c.w||b!==c.h){d.trigger(m,[c.w=a,c.h=b])}});q()},s[v])}})(jQuery,this);(function(b){var a={};function c(f){function e(){var h=f.getPlaceholder();if(h.width()==0||h.height()==0){return}f.resize();f.setupGrid();f.draw()}function g(i,h){i.getPlaceholder().resize(e)}function d(i,h){i.getPlaceholder().unbind("resize",e)}f.hooks.bindEvents.push(g);f.hooks.shutdown.push(d)}b.plot.plugins.push({init:c,options:a,name:"resize",version:"1.0"})})(jQuery); |
/* | |
Flot plugin for selecting regions. | |
The plugin defines the following options: | |
selection: { | |
mode: null or "x" or "y" or "xy", | |
color: color | |
} | |
Selection support is enabled by setting the mode to one of "x", "y" or | |
"xy". In "x" mode, the user will only be able to specify the x range, | |
similarly for "y" mode. For "xy", the selection becomes a rectangle | |
where both ranges can be specified. "color" is color of the selection | |
(if you need to change the color later on, you can get to it with | |
plot.getOptions().selection.color). | |
When selection support is enabled, a "plotselected" event will be | |
emitted on the DOM element you passed into the plot function. The | |
event handler gets a parameter with the ranges selected on the axes, | |
like this: | |
placeholder.bind("plotselected", function(event, ranges) { | |
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to) | |
// similar for yaxis - with multiple axes, the extra ones are in | |
// x2axis, x3axis, ... | |
}); | |
The "plotselected" event is only fired when the user has finished | |
making the selection. A "plotselecting" event is fired during the | |
process with the same parameters as the "plotselected" event, in case | |
you want to know what's happening while it's happening, | |
A "plotunselected" event with no arguments is emitted when the user | |
clicks the mouse to remove the selection. | |
The plugin allso adds the following methods to the plot object: | |
- setSelection(ranges, preventEvent) | |
Set the selection rectangle. The passed in ranges is on the same | |
form as returned in the "plotselected" event. If the selection mode | |
is "x", you should put in either an xaxis range, if the mode is "y" | |
you need to put in an yaxis range and both xaxis and yaxis if the | |
selection mode is "xy", like this: | |
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } }); | |
setSelection will trigger the "plotselected" event when called. If | |
you don't want that to happen, e.g. if you're inside a | |
"plotselected" handler, pass true as the second parameter. If you | |
are using multiple axes, you can specify the ranges on any of those, | |
e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the | |
first one it sees. | |
- clearSelection(preventEvent) | |
Clear the selection rectangle. Pass in true to avoid getting a | |
"plotunselected" event. | |
- getSelection() | |
Returns the current selection in the same format as the | |
"plotselected" event. If there's currently no selection, the | |
function returns null. | |
*/ | |
(function ($) { | |
function init(plot) { | |
var selection = { | |
first: { x: -1, y: -1}, second: { x: -1, y: -1}, | |
show: false, | |
active: false | |
}; | |
// FIXME: The drag handling implemented here should be | |
// abstracted out, there's some similar code from a library in | |
// the navigation plugin, this should be massaged a bit to fit | |
// the Flot cases here better and reused. Doing this would | |
// make this plugin much slimmer. | |
var savedhandlers = {}; | |
var mouseUpHandler = null; | |
function onMouseMove(e) { | |
if (selection.active) { | |
updateSelection(e); | |
plot.getPlaceholder().trigger("plotselecting", [ getSelection() ]); | |
} | |
} | |
function onMouseDown(e) { | |
if (e.which != 1) // only accept left-click | |
return; | |
// cancel out any text selections | |
document.body.focus(); | |
// prevent text selection and drag in old-school browsers | |
if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) { | |
savedhandlers.onselectstart = document.onselectstart; | |
document.onselectstart = function () { return false; }; | |
} | |
if (document.ondrag !== undefined && savedhandlers.ondrag == null) { | |
savedhandlers.ondrag = document.ondrag; | |
document.ondrag = function () { return false; }; | |
} | |
setSelectionPos(selection.first, e); | |
selection.active = true; | |
// this is a bit silly, but we have to use a closure to be | |
// able to whack the same handler again | |
mouseUpHandler = function (e) { onMouseUp(e); }; | |
$(document).one("mouseup", mouseUpHandler); | |
} | |
function onMouseUp(e) { | |
mouseUpHandler = null; | |
// revert drag stuff for old-school browsers | |
if (document.onselectstart !== undefined) | |
document.onselectstart = savedhandlers.onselectstart; | |
if (document.ondrag !== undefined) | |
document.ondrag = savedhandlers.ondrag; | |
// no more dragging | |
selection.active = false; | |
updateSelection(e); | |
if (selectionIsSane()) | |
triggerSelectedEvent(); | |
else { | |
// this counts as a clear | |
plot.getPlaceholder().trigger("plotunselected", [ ]); | |
plot.getPlaceholder().trigger("plotselecting", [ null ]); | |
} | |
return false; | |
} | |
function getSelection() { | |
if (!selectionIsSane()) | |
return null; | |
var r = {}, c1 = selection.first, c2 = selection.second; | |
$.each(plot.getAxes(), function (name, axis) { | |
if (axis.used) { | |
var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); | |
r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) }; | |
} | |
}); | |
return r; | |
} | |
function triggerSelectedEvent() { | |
var r = getSelection(); | |
plot.getPlaceholder().trigger("plotselected", [ r ]); | |
// backwards-compat stuff, to be removed in future | |
if (r.xaxis && r.yaxis) | |
plot.getPlaceholder().trigger("selected", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]); | |
} | |
function clamp(min, value, max) { | |
return value < min ? min: (value > max ? max: value); | |
} | |
function setSelectionPos(pos, e) { | |
var o = plot.getOptions(); | |
var offset = plot.getPlaceholder().offset(); | |
var plotOffset = plot.getPlotOffset(); | |
pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width()); | |
pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height()); | |
if (o.selection.mode == "y") | |
pos.x = pos == selection.first ? 0 : plot.width(); | |
if (o.selection.mode == "x") | |
pos.y = pos == selection.first ? 0 : plot.height(); | |
} | |
function updateSelection(pos) { | |
if (pos.pageX == null) | |
return; | |
setSelectionPos(selection.second, pos); | |
if (selectionIsSane()) { | |
selection.show = true; | |
plot.triggerRedrawOverlay(); | |
} | |
else | |
clearSelection(true); | |
} | |
function clearSelection(preventEvent) { | |
if (selection.show) { | |
selection.show = false; | |
plot.triggerRedrawOverlay(); | |
if (!preventEvent) | |
plot.getPlaceholder().trigger("plotunselected", [ ]); | |
} | |
} | |
// function taken from markings support in Flot | |
function extractRange(ranges, coord) { | |
var axis, from, to, key, axes = plot.getAxes(); | |
for (var k in axes) { | |
axis = axes[k]; | |
if (axis.direction == coord) { | |
key = coord + axis.n + "axis"; | |
if (!ranges[key] && axis.n == 1) | |
key = coord + "axis"; // support x1axis as xaxis | |
if (ranges[key]) { | |
from = ranges[key].from; | |
to = ranges[key].to; | |
break; | |
} | |
} | |
} | |
// backwards-compat stuff - to be removed in future | |
if (!ranges[key]) { | |
axis = coord == "x" ? plot.getXAxes()[0] : plot.getYAxes()[0]; | |
from = ranges[coord + "1"]; | |
to = ranges[coord + "2"]; | |
} | |
// auto-reverse as an added bonus | |
if (from != null && to != null && from > to) { | |
var tmp = from; | |
from = to; | |
to = tmp; | |
} | |
return { from: from, to: to, axis: axis }; | |
} | |
function setSelection(ranges, preventEvent) { | |
var axis, range, o = plot.getOptions(); | |
if (o.selection.mode == "y") { | |
selection.first.x = 0; | |
selection.second.x = plot.width(); | |
} | |
else { | |
range = extractRange(ranges, "x"); | |
selection.first.x = range.axis.p2c(range.from); | |
selection.second.x = range.axis.p2c(range.to); | |
} | |
if (o.selection.mode == "x") { | |
selection.first.y = 0; | |
selection.second.y = plot.height(); | |
} | |
else { | |
range = extractRange(ranges, "y"); | |
selection.first.y = range.axis.p2c(range.from); | |
selection.second.y = range.axis.p2c(range.to); | |
} | |
selection.show = true; | |
plot.triggerRedrawOverlay(); | |
if (!preventEvent && selectionIsSane()) | |
triggerSelectedEvent(); | |
} | |
function selectionIsSane() { | |
var minSize = 5; | |
return Math.abs(selection.second.x - selection.first.x) >= minSize && | |
Math.abs(selection.second.y - selection.first.y) >= minSize; | |
} | |
plot.clearSelection = clearSelection; | |
plot.setSelection = setSelection; | |
plot.getSelection = getSelection; | |
plot.hooks.bindEvents.push(function(plot, eventHolder) { | |
var o = plot.getOptions(); | |
if (o.selection.mode != null) { | |
eventHolder.mousemove(onMouseMove); | |
eventHolder.mousedown(onMouseDown); | |
} | |
}); | |
plot.hooks.drawOverlay.push(function (plot, ctx) { | |
// draw selection | |
if (selection.show && selectionIsSane()) { | |
var plotOffset = plot.getPlotOffset(); | |
var o = plot.getOptions(); | |
ctx.save(); | |
ctx.translate(plotOffset.left, plotOffset.top); | |
var c = $.color.parse(o.selection.color); | |
ctx.strokeStyle = c.scale('a', 0.8).toString(); | |
ctx.lineWidth = 1; | |
ctx.lineJoin = "round"; | |
ctx.fillStyle = c.scale('a', 0.4).toString(); | |
var x = Math.min(selection.first.x, selection.second.x), | |
y = Math.min(selection.first.y, selection.second.y), | |
w = Math.abs(selection.second.x - selection.first.x), | |
h = Math.abs(selection.second.y - selection.first.y); | |
ctx.fillRect(x, y, w, h); | |
ctx.strokeRect(x, y, w, h); | |
ctx.restore(); | |
} | |
}); | |
plot.hooks.shutdown.push(function (plot, eventHolder) { | |
eventHolder.unbind("mousemove", onMouseMove); | |
eventHolder.unbind("mousedown", onMouseDown); | |
if (mouseUpHandler) | |
$(document).unbind("mouseup", mouseUpHandler); | |
}); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: { | |
selection: { | |
mode: null, // one of null, "x", "y" or "xy" | |
color: "#e8cfac" | |
} | |
}, | |
name: 'selection', | |
version: '1.1' | |
}); | |
})(jQuery); | |
(function(a){function b(k){var p={first:{x:-1,y:-1},second:{x:-1,y:-1},show:false,active:false};var m={};var r=null;function e(s){if(p.active){l(s);k.getPlaceholder().trigger("plotselecting",[g()])}}function n(s){if(s.which!=1){return}document.body.focus();if(document.onselectstart!==undefined&&m.onselectstart==null){m.onselectstart=document.onselectstart;document.onselectstart=function(){return false}}if(document.ondrag!==undefined&&m.ondrag==null){m.ondrag=document.ondrag;document.ondrag=function(){return false}}d(p.first,s);p.active=true;r=function(t){j(t)};a(document).one("mouseup",r)}function j(s){r=null;if(document.onselectstart!==undefined){document.onselectstart=m.onselectstart}if(document.ondrag!==undefined){document.ondrag=m.ondrag}p.active=false;l(s);if(f()){i()}else{k.getPlaceholder().trigger("plotunselected",[]);k.getPlaceholder().trigger("plotselecting",[null])}return false}function g(){if(!f()){return null}var u={},t=p.first,s=p.second;a.each(k.getAxes(),function(v,w){if(w.used){var y=w.c2p(t[w.direction]),x=w.c2p(s[w.direction]);u[v]={from:Math.min(y,x),to:Math.max(y,x)}}});return u}function i(){var s=g();k.getPlaceholder().trigger("plotselected",[s]);if(s.xaxis&&s.yaxis){k.getPlaceholder().trigger("selected",[{x1:s.xaxis.from,y1:s.yaxis.from,x2:s.xaxis.to,y2:s.yaxis.to}])}}function h(t,u,s){return u<t?t:(u>s?s:u)}function d(w,t){var v=k.getOptions();var u=k.getPlaceholder().offset();var s=k.getPlotOffset();w.x=h(0,t.pageX-u.left-s.left,k.width());w.y=h(0,t.pageY-u.top-s.top,k.height());if(v.selection.mode=="y"){w.x=w==p.first?0:k.width()}if(v.selection.mode=="x"){w.y=w==p.first?0:k.height()}}function l(s){if(s.pageX==null){return}d(p.second,s);if(f()){p.show=true;k.triggerRedrawOverlay()}else{q(true)}}function q(s){if(p.show){p.show=false;k.triggerRedrawOverlay();if(!s){k.getPlaceholder().trigger("plotunselected",[])}}}function c(s,w){var t,y,z,A,x=k.getAxes();for(var u in x){t=x[u];if(t.direction==w){A=w+t.n+"axis";if(!s[A]&&t.n==1){A=w+"axis"}if(s[A]){y=s[A].from;z=s[A].to;break}}}if(!s[A]){t=w=="x"?k.getXAxes()[0]:k.getYAxes()[0];y=s[w+"1"];z=s[w+"2"]}if(y!=null&&z!=null&&y>z){var v=y;y=z;z=v}return{from:y,to:z,axis:t}}function o(t,s){var v,u,w=k.getOptions();if(w.selection.mode=="y"){p.first.x=0;p.second.x=k.width()}else{u=c(t,"x");p.first.x=u.axis.p2c(u.from);p.second.x=u.axis.p2c(u.to)}if(w.selection.mode=="x"){p.first.y=0;p.second.y=k.height()}else{u=c(t,"y");p.first.y=u.axis.p2c(u.from);p.second.y=u.axis.p2c(u.to)}p.show=true;k.triggerRedrawOverlay();if(!s&&f()){i()}}function f(){var s=5;return Math.abs(p.second.x-p.first.x)>=s&&Math.abs(p.second.y-p.first.y)>=s}k.clearSelection=q;k.setSelection=o;k.getSelection=g;k.hooks.bindEvents.push(function(t,s){var u=t.getOptions();if(u.selection.mode!=null){s.mousemove(e);s.mousedown(n)}});k.hooks.drawOverlay.push(function(v,D){if(p.show&&f()){var t=v.getPlotOffset();var s=v.getOptions();D.save();D.translate(t.left,t.top);var z=a.color.parse(s.selection.color);D.strokeStyle=z.scale("a",0.8).toString();D.lineWidth=1;D.lineJoin="round";D.fillStyle=z.scale("a",0.4).toString();var B=Math.min(p.first.x,p.second.x),A=Math.min(p.first.y,p.second.y),C=Math.abs(p.second.x-p.first.x),u=Math.abs(p.second.y-p.first.y);D.fillRect(B,A,C,u);D.strokeRect(B,A,C,u);D.restore()}});k.hooks.shutdown.push(function(t,s){s.unbind("mousemove",e);s.unbind("mousedown",n);if(r){a(document).unbind("mouseup",r)}})}a.plot.plugins.push({init:b,options:{selection:{mode:null,color:"#e8cfac"}},name:"selection",version:"1.1"})})(jQuery); |
/* | |
Flot plugin for stacking data sets, i.e. putting them on top of each | |
other, for accumulative graphs. | |
The plugin assumes the data is sorted on x (or y if stacking | |
horizontally). For line charts, it is assumed that if a line has an | |
undefined gap (from a null point), then the line above it should have | |
the same gap - insert zeros instead of "null" if you want another | |
behaviour. This also holds for the start and end of the chart. Note | |
that stacking a mix of positive and negative values in most instances | |
doesn't make sense (so it looks weird). | |
Two or more series are stacked when their "stack" attribute is set to | |
the same key (which can be any number or string or just "true"). To | |
specify the default stack, you can set | |
series: { | |
stack: null or true or key (number/string) | |
} | |
or specify it for a specific series | |
$.plot($("#placeholder"), [{ data: [ ... ], stack: true }]) | |
The stacking order is determined by the order of the data series in | |
the array (later series end up on top of the previous). | |
Internally, the plugin modifies the datapoints in each series, adding | |
an offset to the y value. For line series, extra data points are | |
inserted through interpolation. If there's a second y value, it's also | |
adjusted (e.g for bar charts or filled areas). | |
*/ | |
(function ($) { | |
var options = { | |
series: { stack: null } // or number/string | |
}; | |
function init(plot) { | |
function findMatchingSeries(s, allseries) { | |
var res = null | |
for (var i = 0; i < allseries.length; ++i) { | |
if (s == allseries[i]) | |
break; | |
if (allseries[i].stack == s.stack) | |
res = allseries[i]; | |
} | |
return res; | |
} | |
function stackData(plot, s, datapoints) { | |
if (s.stack == null) | |
return; | |
var other = findMatchingSeries(s, plot.getData()); | |
if (!other) | |
return; | |
var ps = datapoints.pointsize, | |
points = datapoints.points, | |
otherps = other.datapoints.pointsize, | |
otherpoints = other.datapoints.points, | |
newpoints = [], | |
px, py, intery, qx, qy, bottom, | |
withlines = s.lines.show, | |
horizontal = s.bars.horizontal, | |
withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y), | |
withsteps = withlines && s.lines.steps, | |
fromgap = true, | |
keyOffset = horizontal ? 1 : 0, | |
accumulateOffset = horizontal ? 0 : 1, | |
i = 0, j = 0, l; | |
while (true) { | |
if (i >= points.length) | |
break; | |
l = newpoints.length; | |
if (points[i] == null) { | |
// copy gaps | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
i += ps; | |
} | |
else if (j >= otherpoints.length) { | |
// for lines, we can't use the rest of the points | |
if (!withlines) { | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
} | |
i += ps; | |
} | |
else if (otherpoints[j] == null) { | |
// oops, got a gap | |
for (m = 0; m < ps; ++m) | |
newpoints.push(null); | |
fromgap = true; | |
j += otherps; | |
} | |
else { | |
// cases where we actually got two points | |
px = points[i + keyOffset]; | |
py = points[i + accumulateOffset]; | |
qx = otherpoints[j + keyOffset]; | |
qy = otherpoints[j + accumulateOffset]; | |
bottom = 0; | |
if (px == qx) { | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
newpoints[l + accumulateOffset] += qy; | |
bottom = qy; | |
i += ps; | |
j += otherps; | |
} | |
else if (px > qx) { | |
// we got past point below, might need to | |
// insert interpolated extra point | |
if (withlines && i > 0 && points[i - ps] != null) { | |
intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px); | |
newpoints.push(qx); | |
newpoints.push(intery + qy); | |
for (m = 2; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
bottom = qy; | |
} | |
j += otherps; | |
} | |
else { // px < qx | |
if (fromgap && withlines) { | |
// if we come from a gap, we just skip this point | |
i += ps; | |
continue; | |
} | |
for (m = 0; m < ps; ++m) | |
newpoints.push(points[i + m]); | |
// we might be able to interpolate a point below, | |
// this can give us a better y | |
if (withlines && j > 0 && otherpoints[j - otherps] != null) | |
bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx); | |
newpoints[l + accumulateOffset] += bottom; | |
i += ps; | |
} | |
fromgap = false; | |
if (l != newpoints.length && withbottom) | |
newpoints[l + 2] += bottom; | |
} | |
// maintain the line steps invariant | |
if (withsteps && l != newpoints.length && l > 0 | |
&& newpoints[l] != null | |
&& newpoints[l] != newpoints[l - ps] | |
&& newpoints[l + 1] != newpoints[l - ps + 1]) { | |
for (m = 0; m < ps; ++m) | |
newpoints[l + ps + m] = newpoints[l + m]; | |
newpoints[l + 1] = newpoints[l - ps + 1]; | |
} | |
} | |
datapoints.points = newpoints; | |
} | |
plot.hooks.processDatapoints.push(stackData); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'stack', | |
version: '1.2' | |
}); | |
})(jQuery); | |
(function(b){var a={series:{stack:null}};function c(f){function d(k,j){var h=null;for(var g=0;g<j.length;++g){if(k==j[g]){break}if(j[g].stack==k.stack){h=j[g]}}return h}function e(C,v,g){if(v.stack==null){return}var p=d(v,C.getData());if(!p){return}var z=g.pointsize,F=g.points,h=p.datapoints.pointsize,y=p.datapoints.points,t=[],x,w,k,J,I,r,u=v.lines.show,G=v.bars.horizontal,o=z>2&&(G?g.format[2].x:g.format[2].y),n=u&&v.lines.steps,E=true,q=G?1:0,H=G?0:1,D=0,B=0,A;while(true){if(D>=F.length){break}A=t.length;if(F[D]==null){for(m=0;m<z;++m){t.push(F[D+m])}D+=z}else{if(B>=y.length){if(!u){for(m=0;m<z;++m){t.push(F[D+m])}}D+=z}else{if(y[B]==null){for(m=0;m<z;++m){t.push(null)}E=true;B+=h}else{x=F[D+q];w=F[D+H];J=y[B+q];I=y[B+H];r=0;if(x==J){for(m=0;m<z;++m){t.push(F[D+m])}t[A+H]+=I;r=I;D+=z;B+=h}else{if(x>J){if(u&&D>0&&F[D-z]!=null){k=w+(F[D-z+H]-w)*(J-x)/(F[D-z+q]-x);t.push(J);t.push(k+I);for(m=2;m<z;++m){t.push(F[D+m])}r=I}B+=h}else{if(E&&u){D+=z;continue}for(m=0;m<z;++m){t.push(F[D+m])}if(u&&B>0&&y[B-h]!=null){r=I+(y[B-h+H]-I)*(x-J)/(y[B-h+q]-J)}t[A+H]+=r;D+=z}}E=false;if(A!=t.length&&o){t[A+2]+=r}}}}if(n&&A!=t.length&&A>0&&t[A]!=null&&t[A]!=t[A-z]&&t[A+1]!=t[A-z+1]){for(m=0;m<z;++m){t[A+z+m]=t[A+m]}t[A+1]=t[A-z+1]}}g.points=t}f.hooks.processDatapoints.push(e)}b.plot.plugins.push({init:c,options:a,name:"stack",version:"1.2"})})(jQuery); |
/* | |
Flot plugin that adds some extra symbols for plotting points. | |
The symbols are accessed as strings through the standard symbol | |
choice: | |
series: { | |
points: { | |
symbol: "square" // or "diamond", "triangle", "cross" | |
} | |
} | |
*/ | |
(function ($) { | |
function processRawData(plot, series, datapoints) { | |
// we normalize the area of each symbol so it is approximately the | |
// same as a circle of the given radius | |
var handlers = { | |
square: function (ctx, x, y, radius, shadow) { | |
// pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 | |
var size = radius * Math.sqrt(Math.PI) / 2; | |
ctx.rect(x - size, y - size, size + size, size + size); | |
}, | |
diamond: function (ctx, x, y, radius, shadow) { | |
// pi * r^2 = 2s^2 => s = r * sqrt(pi/2) | |
var size = radius * Math.sqrt(Math.PI / 2); | |
ctx.moveTo(x - size, y); | |
ctx.lineTo(x, y - size); | |
ctx.lineTo(x + size, y); | |
ctx.lineTo(x, y + size); | |
ctx.lineTo(x - size, y); | |
}, | |
triangle: function (ctx, x, y, radius, shadow) { | |
// pi * r^2 = 1/2 * s^2 * sin (pi / 3) => s = r * sqrt(2 * pi / sin(pi / 3)) | |
var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3)); | |
var height = size * Math.sin(Math.PI / 3); | |
ctx.moveTo(x - size/2, y + height/2); | |
ctx.lineTo(x + size/2, y + height/2); | |
if (!shadow) { | |
ctx.lineTo(x, y - height/2); | |
ctx.lineTo(x - size/2, y + height/2); | |
} | |
}, | |
cross: function (ctx, x, y, radius, shadow) { | |
// pi * r^2 = (2s)^2 => s = r * sqrt(pi)/2 | |
var size = radius * Math.sqrt(Math.PI) / 2; | |
ctx.moveTo(x - size, y - size); | |
ctx.lineTo(x + size, y + size); | |
ctx.moveTo(x - size, y + size); | |
ctx.lineTo(x + size, y - size); | |
} | |
} | |
var s = series.points.symbol; | |
if (handlers[s]) | |
series.points.symbol = handlers[s]; | |
} | |
function init(plot) { | |
plot.hooks.processDatapoints.push(processRawData); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
name: 'symbols', | |
version: '1.0' | |
}); | |
})(jQuery); | |
(function(b){function a(h,e,g){var d={square:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.rect(j-l,n-l,l+l,l+l)},diamond:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI/2);k.moveTo(j-l,n);k.lineTo(j,n-l);k.lineTo(j+l,n);k.lineTo(j,n+l);k.lineTo(j-l,n)},triangle:function(l,k,o,j,n){var m=j*Math.sqrt(2*Math.PI/Math.sin(Math.PI/3));var i=m*Math.sin(Math.PI/3);l.moveTo(k-m/2,o+i/2);l.lineTo(k+m/2,o+i/2);if(!n){l.lineTo(k,o-i/2);l.lineTo(k-m/2,o+i/2)}},cross:function(k,j,n,i,m){var l=i*Math.sqrt(Math.PI)/2;k.moveTo(j-l,n-l);k.lineTo(j+l,n+l);k.moveTo(j-l,n+l);k.lineTo(j+l,n-l)}};var f=e.points.symbol;if(d[f]){e.points.symbol=d[f]}}function c(d){d.hooks.processDatapoints.push(a)}b.plot.plugins.push({init:c,name:"symbols",version:"1.0"})})(jQuery); |
/* | |
Flot plugin for thresholding data. Controlled through the option | |
"threshold" in either the global series options | |
series: { | |
threshold: { | |
below: number | |
color: colorspec | |
} | |
} | |
or in a specific series | |
$.plot($("#placeholder"), [{ data: [ ... ], threshold: { ... }}]) | |
The data points below "below" are drawn with the specified color. This | |
makes it easy to mark points below 0, e.g. for budget data. | |
Internally, the plugin works by splitting the data into two series, | |
above and below the threshold. The extra series below the threshold | |
will have its label cleared and the special "originSeries" attribute | |
set to the original series. You may need to check for this in hover | |
events. | |
*/ | |
(function ($) { | |
var options = { | |
series: { threshold: null } // or { below: number, color: color spec} | |
}; | |
function init(plot) { | |
function thresholdData(plot, s, datapoints) { | |
if (!s.threshold) | |
return; | |
var ps = datapoints.pointsize, i, x, y, p, prevp, | |
thresholded = $.extend({}, s); // note: shallow copy | |
thresholded.datapoints = { points: [], pointsize: ps }; | |
thresholded.label = null; | |
thresholded.color = s.threshold.color; | |
thresholded.threshold = null; | |
thresholded.originSeries = s; | |
thresholded.data = []; | |
var below = s.threshold.below, | |
origpoints = datapoints.points, | |
addCrossingPoints = s.lines.show; | |
threspoints = []; | |
newpoints = []; | |
for (i = 0; i < origpoints.length; i += ps) { | |
x = origpoints[i] | |
y = origpoints[i + 1]; | |
prevp = p; | |
if (y < below) | |
p = threspoints; | |
else | |
p = newpoints; | |
if (addCrossingPoints && prevp != p && x != null | |
&& i > 0 && origpoints[i - ps] != null) { | |
var interx = (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]) * (below - y) + x; | |
prevp.push(interx); | |
prevp.push(below); | |
for (m = 2; m < ps; ++m) | |
prevp.push(origpoints[i + m]); | |
p.push(null); // start new segment | |
p.push(null); | |
for (m = 2; m < ps; ++m) | |
p.push(origpoints[i + m]); | |
p.push(interx); | |
p.push(below); | |
for (m = 2; m < ps; ++m) | |
p.push(origpoints[i + m]); | |
} | |
p.push(x); | |
p.push(y); | |
} | |
datapoints.points = newpoints; | |
thresholded.datapoints.points = threspoints; | |
if (thresholded.datapoints.points.length > 0) | |
plot.getData().push(thresholded); | |
// FIXME: there are probably some edge cases left in bars | |
} | |
plot.hooks.processDatapoints.push(thresholdData); | |
} | |
$.plot.plugins.push({ | |
init: init, | |
options: options, | |
name: 'threshold', | |
version: '1.0' | |
}); | |
})(jQuery); | |
(function(B){var A={series:{threshold:null}};function C(D){function E(L,S,M){if(!S.threshold){return }var F=M.pointsize,I,O,N,G,K,H=B.extend({},S);H.datapoints={points:[],pointsize:F};H.label=null;H.color=S.threshold.color;H.threshold=null;H.originSeries=S;H.data=[];var P=S.threshold.below,Q=M.points,R=S.lines.show;threspoints=[];newpoints=[];for(I=0;I<Q.length;I+=F){O=Q[I];N=Q[I+1];K=G;if(N<P){G=threspoints}else{G=newpoints}if(R&&K!=G&&O!=null&&I>0&&Q[I-F]!=null){var J=(O-Q[I-F])/(N-Q[I-F+1])*(P-N)+O;K.push(J);K.push(P);for(m=2;m<F;++m){K.push(Q[I+m])}G.push(null);G.push(null);for(m=2;m<F;++m){G.push(Q[I+m])}G.push(J);G.push(P);for(m=2;m<F;++m){G.push(Q[I+m])}}G.push(O);G.push(N)}M.points=newpoints;H.datapoints.points=threspoints;if(H.datapoints.points.length>0){L.getData().push(H)}}D.hooks.processDatapoints.push(E)}B.plot.plugins.push({init:C,options:A,name:"threshold",version:"1.0"})})(jQuery); |
//Flotr 0.2.0-alpha Copyright (c) 2009 Bas Wenneker, <http://solutoire.com>, MIT License. | |
var Flotr={version:"0.2.0-alpha",author:"Bas Wenneker",website:"http://www.solutoire.com",_registeredTypes:{lines:"drawSeriesLines",points:"drawSeriesPoints",bars:"drawSeriesBars",candles:"drawSeriesCandles",pie:"drawSeriesPie"},register:function(A,B){Flotr._registeredTypes[A]=B+""},draw:function(B,D,A,C){C=C||Flotr.Graph;return new C(B,D,A)},getSeries:function(A){return A.collect(function(C){var B,C=(C.data)?Object.clone(C):{data:C};for(B=C.data.length-1;B>-1;--B){C.data[B][1]=(C.data[B][1]===null?null:parseFloat(C.data[B][1]))}return C})},merge:function(D,B){var A=B||{};for(var C in D){A[C]=(D[C]!=null&&typeof (D[C])=="object"&&!(D[C].constructor==Array||D[C].constructor==RegExp)&&!Object.isElement(D[C]))?Flotr.merge(D[C],B[C]):A[C]=D[C]}return A},getTickSize:function(E,D,A,B){var H=(A-D)/E;var G=Flotr.getMagnitude(H);var C=H/G;var F=10;if(C<1.5){F=1}else{if(C<2.25){F=2}else{if(C<3){F=((B==0)?2:2.5)}else{if(C<7.5){F=5}}}}return F*G},defaultTickFormatter:function(A){return A+""},defaultTrackFormatter:function(A){return"("+A.x+", "+A.y+")"},defaultPieLabelFormatter:function(A){return(A.fraction*100).toFixed(2)+"%"},getMagnitude:function(A){return Math.pow(10,Math.floor(Math.log(A)/Math.LN10))},toPixel:function(A){return Math.floor(A)+0.5},toRad:function(A){return -A*(Math.PI/180)},parseColor:function(D){if(D instanceof Flotr.Color){return D}var A,C=Flotr.Color;if((A=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(D))){return new C(parseInt(A[1]),parseInt(A[2]),parseInt(A[3]))}if((A=/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(D))){return new C(parseInt(A[1]),parseInt(A[2]),parseInt(A[3]),parseFloat(A[4]))}if((A=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(D))){return new C(parseFloat(A[1])*2.55,parseFloat(A[2])*2.55,parseFloat(A[3])*2.55)}if((A=/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(D))){return new C(parseFloat(A[1])*2.55,parseFloat(A[2])*2.55,parseFloat(A[3])*2.55,parseFloat(A[4]))}if((A=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(D))){return new C(parseInt(A[1],16),parseInt(A[2],16),parseInt(A[3],16))}if((A=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(D))){return new C(parseInt(A[1]+A[1],16),parseInt(A[2]+A[2],16),parseInt(A[3]+A[3],16))}var B=D.strip().toLowerCase();if(B=="transparent"){return new C(255,255,255,0)}return((A=C.lookupColors[B]))?new C(A[0],A[1],A[2]):false},extractColor:function(B){var A;do{A=B.getStyle("background-color").toLowerCase();if(!(A==""||A=="transparent")){break}B=B.up(0)}while(!B.nodeName.match(/^body$/i));return(A=="rgba(0, 0, 0, 0)")?"transparent":A}};Flotr.Graph=Class.create({initialize:function(B,C,A){this.el=$(B);if(!this.el){throw"The target container doesn't exist"}this.data=C;this.series=Flotr.getSeries(C);this.setOptions(A);this.lastMousePos={pageX:null,pageY:null};this.selection={first:{x:-1,y:-1},second:{x:-1,y:-1}};this.prevSelection=null;this.selectionInterval=null;this.ignoreClick=false;this.prevHit=null;this.constructCanvas();this.initEvents();this.findDataRanges();this.calculateTicks(this.axes.x);this.calculateTicks(this.axes.x2);this.calculateTicks(this.axes.y);this.calculateTicks(this.axes.y2);this.calculateSpacing();this.draw();this.insertLegend();if(this.options.spreadsheet.show){this.constructTabs()}},setOptions:function(B){var P={colors:["#00A8F0","#C0D800","#CB4B4B","#4DA74D","#9440ED"],title:null,subtitle:null,legend:{show:true,noColumns:1,labelFormatter:Prototype.K,labelBoxBorderColor:"#CCCCCC",labelBoxWidth:14,labelBoxHeight:10,labelBoxMargin:5,container:null,position:"nw",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{ticks:null,showLabels:true,labelsAngle:0,title:null,titleAngle:0,noTicks:5,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscaleMargin:0,color:null},x2axis:{},yaxis:{ticks:null,showLabels:true,labelsAngle:0,title:null,titleAngle:90,noTicks:5,tickFormatter:Flotr.defaultTickFormatter,tickDecimals:null,min:null,max:null,autoscaleMargin:0,color:null},y2axis:{titleAngle:270},points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#FFFFFF",fillOpacity:0.4},lines:{show:false,lineWidth:2,fill:false,fillColor:null,fillOpacity:0.4},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null,fillOpacity:0.4,horizontal:false,stacked:false},candles:{show:false,lineWidth:1,wickLineWidth:1,candleWidth:0.6,fill:true,upFillColor:"#00A8F0",downFillColor:"#CB4B4B",fillOpacity:0.5,barcharts:false},pie:{show:false,lineWidth:1,fill:true,fillColor:null,fillOpacity:0.6,explode:6,sizeRatio:0.6,startAngle:Math.PI/4,labelFormatter:Flotr.defaultPieLabelFormatter,pie3D:false,pie3DviewAngle:(Math.PI/2*0.8),pie3DspliceThickness:20},grid:{color:"#545454",backgroundColor:null,tickColor:"#DDDDDD",labelMargin:3,verticalLines:true,horizontalLines:true,outlineWidth:2},selection:{mode:null,color:"#B6D9FF",fps:20},mouse:{track:false,position:"se",relative:false,trackFormatter:Flotr.defaultTrackFormatter,margin:5,lineColor:"#FF3F19",trackDecimals:1,sensibility:2,radius:3},shadowSize:4,defaultType:"lines",HtmlText:true,fontSize:7.5,spreadsheet:{show:false,tabGraphLabel:"Graph",tabDataLabel:"Data",toolbarDownload:"Download CSV",toolbarSelectAll:"Select all"}};P.x2axis=Object.extend(Object.clone(P.xaxis),P.x2axis);P.y2axis=Object.extend(Object.clone(P.yaxis),P.y2axis);this.options=Flotr.merge((B||{}),P);this.axes={x:{options:this.options.xaxis,n:1},x2:{options:this.options.x2axis,n:2},y:{options:this.options.yaxis,n:1},y2:{options:this.options.y2axis,n:2}};var H=[],C=[],K=this.series.length,N=this.series.length,D=this.options.colors,A=[],G=0,M,J,I,O,E;for(J=N-1;J>-1;--J){M=this.series[J].color;if(M!=null){--N;if(Object.isNumber(M)){H.push(M)}else{A.push(Flotr.parseColor(M))}}}for(J=H.length-1;J>-1;--J){N=Math.max(N,H[J]+1)}for(J=0;C.length<N;){M=(D.length==J)?new Flotr.Color(100,100,100):Flotr.parseColor(D[J]);var F=G%2==1?-1:1;var L=1+F*Math.ceil(G/2)*0.2;M.scale(L,L,L);C.push(M);if(++J>=D.length){J=0;++G}}for(J=0,I=0;J<K;++J){O=this.series[J];if(O.color==null){O.color=C[I++].toString()}else{if(Object.isNumber(O.color)){O.color=C[O.color].toString()}}if(!O.xaxis){O.xaxis=this.axes.x}if(O.xaxis==1){O.xaxis=this.axes.x}else{if(O.xaxis==2){O.xaxis=this.axes.x2}}if(!O.yaxis){O.yaxis=this.axes.y}if(O.yaxis==1){O.yaxis=this.axes.y}else{if(O.yaxis==2){O.yaxis=this.axes.y2}}O.lines=Object.extend(Object.clone(this.options.lines),O.lines);O.points=Object.extend(Object.clone(this.options.points),O.points);O.bars=Object.extend(Object.clone(this.options.bars),O.bars);O.candles=Object.extend(Object.clone(this.options.candles),O.candles);O.pie=Object.extend(Object.clone(this.options.pie),O.pie);O.mouse=Object.extend(Object.clone(this.options.mouse),O.mouse);if(O.shadowSize==null){O.shadowSize=this.options.shadowSize}}},constructCanvas:function(){var C=this.el,B,D,A;this.canvas=C.select(".flotr-canvas")[0];this.overlay=C.select(".flotr-overlay")[0];C.childElements().invoke("remove");C.setStyle({position:"relative",cursor:"default"});this.canvasWidth=C.getWidth();this.canvasHeight=C.getHeight();B={width:this.canvasWidth,height:this.canvasHeight};if(this.canvasWidth<=0||this.canvasHeight<=0){throw"Invalid dimensions for plot, width = "+this.canvasWidth+", height = "+this.canvasHeight}if(!this.canvas){D=this.canvas=new Element("canvas",B);D.className="flotr-canvas";D=D.writeAttribute("style","position:absolute;left:0px;top:0px;")}else{D=this.canvas.writeAttribute(B)}C.insert(D);if(Prototype.Browser.IE){D=window.G_vmlCanvasManager.initElement(D)}this.ctx=D.getContext("2d");if(!this.overlay){A=this.overlay=new Element("canvas",B);A.className="flotr-overlay";A=A.writeAttribute("style","position:absolute;left:0px;top:0px;")}else{A=this.overlay.writeAttribute(B)}C.insert(A);if(Prototype.Browser.IE){A=window.G_vmlCanvasManager.initElement(A)}this.octx=A.getContext("2d");if(window.CanvasText){CanvasText.enable(this.ctx);CanvasText.enable(this.octx);this.textEnabled=true}},getTextDimensions:function(F,C,B,D){if(!F){return{width:0,height:0}}if(!this.options.HtmlText&&this.textEnabled){var E=this.ctx.getTextBounds(F,C);return{width:E.width+2,height:E.height+6}}else{var A=this.el.insert('<div style="position:absolute;top:-10000px;'+B+'" class="'+D+' flotr-dummy-div">'+F+"</div>").select(".flotr-dummy-div")[0];dim=A.getDimensions();A.remove();return dim}},loadDataGrid:function(){if(this.seriesData){return this.seriesData}var A=this.series;var B=[];for(i=0;i<A.length;++i){A[i].data.each(function(D){var C=D[0],F=D[1];if(r=B.find(function(G){return G[0]==C})){r[i+1]=F}else{var E=[];E[0]=C;E[i+1]=F;B.push(E)}})}B=B.sortBy(function(C){return C[0]});return this.seriesData=B},showTab:function(B,C){var A="canvas, .flotr-labels, .flotr-legend, .flotr-legend-bg, .flotr-title, .flotr-subtitle";switch(B){case"graph":this.datagrid.up().hide();this.el.select(A).invoke("show");this.tabs.data.removeClassName("selected");this.tabs.graph.addClassName("selected");break;case"data":this.constructDataGrid();this.datagrid.up().show();this.el.select(A).invoke("hide");this.tabs.data.addClassName("selected");this.tabs.graph.removeClassName("selected");break}},constructTabs:function(){var A=new Element("div",{className:"flotr-tabs-group",style:"position:absolute;left:0px;top:"+this.canvasHeight+"px;width:"+this.canvasWidth+"px;"});this.el.insert({bottom:A});this.tabs={graph:new Element("div",{className:"flotr-tab selected",style:"float:left;"}).update(this.options.spreadsheet.tabGraphLabel),data:new Element("div",{className:"flotr-tab",style:"float:left;"}).update(this.options.spreadsheet.tabDataLabel)};A.insert(this.tabs.graph).insert(this.tabs.data);this.el.setStyle({height:this.canvasHeight+this.tabs.data.getHeight()+2+"px"});this.tabs.graph.observe("click",(function(){this.showTab("graph")}).bind(this));this.tabs.data.observe("click",(function(){this.showTab("data")}).bind(this))},constructDataGrid:function(){if(this.datagrid){return this.datagrid}var D,B,L=this.series,J=this.loadDataGrid();var K=this.datagrid=new Element("table",{className:"flotr-datagrid",style:"height:100px;"});var C=["<colgroup><col />"];var F=['<tr class="first-row">'];F.push("<th> </th>");for(D=0;D<L.length;++D){F.push('<th scope="col">'+(L[D].label||String.fromCharCode(65+D))+"</th>");C.push("<col />")}F.push("</tr>");for(B=0;B<J.length;++B){F.push("<tr>");for(D=0;D<L.length+1;++D){var M="td";var G=(J[B][D]!=null?Math.round(J[B][D]*100000)/100000:"");if(D==0){M="th";var I;if(this.options.xaxis.ticks){var E=this.options.xaxis.ticks.find(function(N){return N[0]==J[B][D]});if(E){I=E[1]}}else{I=this.options.xaxis.tickFormatter(G)}if(I){G=I}}F.push("<"+M+(M=="th"?' scope="row"':"")+">"+G+"</"+M+">")}F.push("</tr>")}C.push("</colgroup>");K.update(C.join("")+F.join(""));if(!Prototype.Browser.IE){K.select("td").each(function(N){N.observe("mouseover",function(O){N=O.element();var P=N.previousSiblings();K.select("th[scope=col]")[P.length-1].addClassName("hover");K.select("colgroup col")[P.length].addClassName("hover")});N.observe("mouseout",function(){K.select("colgroup col.hover, th.hover").each(function(O){O.removeClassName("hover")})})})}var H=new Element("div",{className:"flotr-datagrid-toolbar"}).insert(new Element("button",{type:"button",className:"flotr-datagrid-toolbar-button"}).update(this.options.spreadsheet.toolbarDownload).observe("click",this.downloadCSV.bind(this))).insert(new Element("button",{type:"button",className:"flotr-datagrid-toolbar-button"}).update(this.options.spreadsheet.toolbarSelectAll).observe("click",this.selectAllData.bind(this)));var A=new Element("div",{className:"flotr-datagrid-container",style:"left:0px;top:0px;width:"+this.canvasWidth+"px;height:"+this.canvasHeight+"px;overflow:auto;"});A.insert(H);K.wrap(A.hide());this.el.insert(A);return K},selectAllData:function(){if(this.tabs){var B,A,E,D,C=this.constructDataGrid();this.showTab("data");(function(){if((E=C.ownerDocument)&&(D=E.defaultView)&&D.getSelection&&E.createRange&&(B=window.getSelection())&&B.removeAllRanges){A=E.createRange();A.selectNode(C);B.removeAllRanges();B.addRange(A)}else{if(document.body&&document.body.createTextRange&&(A=document.body.createTextRange())){A.moveToElementText(C);A.select()}}}).defer();return true}else{return false}},downloadCSV:function(){var D,A='"x"',C=this.series,E=this.loadDataGrid();for(D=0;D<C.length;++D){A+='%09"'+(C[D].label||String.fromCharCode(65+D))+'"'}A+="%0D%0A";for(D=0;D<E.length;++D){if(this.options.xaxis.ticks){var B=this.options.xaxis.ticks.find(function(F){return F[0]==E[D][0]});if(B){E[D][0]=B[1]}}else{E[D][0]=this.options.xaxis.tickFormatter(E[D][0])}A+=E[D].join("%09")+"%0D%0A"}if(Prototype.Browser.IE){A=A.gsub("%09","\t").gsub("%0A","\n").gsub("%0D","\r");window.open().document.write(A)}else{window.open("data:text/csv,"+A)}},initEvents:function(){this.overlay.stopObserving();this.overlay.observe("mousedown",this.mouseDownHandler.bind(this));this.overlay.observe("mousemove",this.mouseMoveHandler.bind(this));this.overlay.observe("click",this.clickHandler.bind(this))},findDataRanges:function(){var J=this.series,G=this.axes;G.x.datamin=0;G.x.datamax=0;G.x2.datamin=0;G.x2.datamax=0;G.y.datamin=0;G.y.datamax=0;G.y2.datamin=0;G.y2.datamax=0;if(J.length>0){var C,A,D,H,F,B,I,E;for(C=0;C<J.length;++C){B=J[C].data,I=J[C].xaxis,E=J[C].yaxis;if(B.length>0&&!J[C].hide){if(!I.used){I.datamin=I.datamax=B[0][0]}if(!E.used){E.datamin=E.datamax=B[0][1]}I.used=true;E.used=true;for(D=B.length-1;D>-1;--D){H=B[D][0];if(H<I.datamin){I.datamin=H}else{if(H>I.datamax){I.datamax=H}}for(A=1;A<B[D].length;A++){F=B[D][A];if(F<E.datamin){E.datamin=F}else{if(F>E.datamax){E.datamax=F}}}}}}}this.findXAxesValues();this.calculateRange(G.x);this.extendXRangeIfNeededByBar(G.x);if(G.x2.used){this.calculateRange(G.x2);this.extendXRangeIfNeededByBar(G.x2)}this.calculateRange(G.y);this.extendYRangeIfNeededByBar(G.y);if(G.y2.used){this.calculateRange(G.y2);this.extendYRangeIfNeededByBar(G.y2)}},calculateRange:function(D){var F=D.options,C=F.min!=null?F.min:D.datamin,A=F.max!=null?F.max:D.datamax,E;if(A-C==0){var B=(A==0)?1:0.01;C-=B;A+=B}D.tickSize=Flotr.getTickSize(F.noTicks,C,A,F.tickDecimals);if(F.min==null){E=F.autoscaleMargin;if(E!=0){C-=D.tickSize*E;if(C<0&&D.datamin>=0){C=0}C=D.tickSize*Math.floor(C/D.tickSize)}}if(F.max==null){E=F.autoscaleMargin;if(E!=0){A+=D.tickSize*E;if(A>0&&D.datamax<=0){A=0}A=D.tickSize*Math.ceil(A/D.tickSize)}}D.min=C;D.max=A},extendXRangeIfNeededByBar:function(A){if(A.options.max==null){var D=A.max,B,I,F,E,H=[],C=null;for(B=0;B<this.series.length;++B){I=this.series[B];F=I.bars;E=I.candles;if(I.axis==A&&(F.show||E.show)){if(!F.horizontal&&(F.barWidth+A.datamax>D)||(E.candleWidth+A.datamax>D)){D=A.max+I.bars.barWidth}if(F.stacked&&F.horizontal){for(j=0;j<I.data.length;j++){if(I.bars.show&&I.bars.stacked){var G=I.data[j][0];H[G]=(H[G]||0)+I.data[j][1];C=I}}for(j=0;j<H.length;j++){D=Math.max(H[j],D)}}}}A.lastSerie=C;A.max=D}},extendYRangeIfNeededByBar:function(A){if(A.options.max==null){var D=A.max,B,I,F,E,H=[],C=null;for(B=0;B<this.series.length;++B){I=this.series[B];F=I.bars;E=I.candles;if(I.yaxis==A&&F.show&&!I.hide){if(F.horizontal&&(F.barWidth+A.datamax>D)||(E.candleWidth+A.datamax>D)){D=A.max+F.barWidth}if(F.stacked&&!F.horizontal){for(j=0;j<I.data.length;j++){if(I.bars.show&&I.bars.stacked){var G=I.data[j][0];H[G]=(H[G]||0)+I.data[j][1];C=I}}for(j=0;j<H.length;j++){D=Math.max(H[j],D)}}}}A.lastSerie=C;A.max=D}},findXAxesValues:function(){for(i=this.series.length-1;i>-1;--i){s=this.series[i];s.xaxis.values=s.xaxis.values||[];for(j=s.data.length-1;j>-1;--j){s.xaxis.values[s.data[j][0]]={}}}},calculateTicks:function(D){var B=D.options,E,H;D.ticks=[];if(B.ticks){var G=B.ticks,I,F;if(Object.isFunction(G)){G=G({min:D.min,max:D.max})}for(E=0;E<G.length;++E){I=G[E];if(typeof (I)=="object"){H=I[0];F=(I.length>1)?I[1]:B.tickFormatter(H)}else{H=I;F=B.tickFormatter(H)}D.ticks[E]={v:H,label:F}}}else{var A=D.tickSize*Math.ceil(D.min/D.tickSize),C;for(E=0;A+E*D.tickSize<=D.max;++E){H=A+E*D.tickSize;C=B.tickDecimals;if(C==null){C=1-Math.floor(Math.log(D.tickSize)/Math.LN10)}if(C<0){C=0}H=H.toFixed(C);D.ticks.push({v:H,label:B.tickFormatter(H)})}}},calculateSpacing:function(){var L=this.axes,N=this.options,H=this.series,D=N.grid.labelMargin,M=L.x,A=L.x2,J=L.y,K=L.y2,F=2,G,E,C,I;[M,A,J,K].each(function(P){var O="";if(P.options.showLabels){for(G=0;G<P.ticks.length;++G){C=P.ticks[G].label.length;if(C>O.length){O=P.ticks[G].label}}}P.maxLabel=this.getTextDimensions(O,{size:N.fontSize,angle:Flotr.toRad(P.options.labelsAngle)},"font-size:smaller;","flotr-grid-label");P.titleSize=this.getTextDimensions(P.options.title,{size:N.fontSize*1.2,angle:Flotr.toRad(P.options.titleAngle)},"font-weight:bold;","flotr-axis-title")},this);I=this.getTextDimensions(N.title,{size:N.fontSize*1.5},"font-size:1em;font-weight:bold;","flotr-title");this.titleHeight=I.height;I=this.getTextDimensions(N.subtitle,{size:N.fontSize},"font-size:smaller;","flotr-subtitle");this.subtitleHeight=I.height;if(N.show){F=Math.max(F,N.points.radius+N.points.lineWidth/2)}for(E=0;E<N.length;++E){if(H[E].points.show){F=Math.max(F,H[E].points.radius+H[E].points.lineWidth/2)}}var B=this.plotOffset={left:0,right:0,top:0,bottom:0};B.left=B.right=B.top=B.bottom=F;B.bottom+=(M.options.showLabels?(M.maxLabel.height+D):0)+(M.options.title?(M.titleSize.height+D):0);B.top+=(A.options.showLabels?(A.maxLabel.height+D):0)+(A.options.title?(A.titleSize.height+D):0)+this.subtitleHeight+this.titleHeight;B.left+=(J.options.showLabels?(J.maxLabel.width+D):0)+(J.options.title?(J.titleSize.width+D):0);B.right+=(K.options.showLabels?(K.maxLabel.width+D):0)+(K.options.title?(K.titleSize.width+D):0);B.top=Math.floor(B.top);this.plotWidth=this.canvasWidth-B.left-B.right;this.plotHeight=this.canvasHeight-B.bottom-B.top;M.scale=this.plotWidth/(M.max-M.min);A.scale=this.plotWidth/(A.max-A.min);J.scale=this.plotHeight/(J.max-J.min);K.scale=this.plotHeight/(K.max-K.min)},draw:function(){this.drawGrid();this.drawLabels();this.drawTitles();if(this.series.length){this.el.fire("flotr:beforedraw",[this.series,this]);for(var A=0;A<this.series.length;A++){if(!this.series[A].hide){this.drawSeries(this.series[A])}}}this.el.fire("flotr:afterdraw",[this.series,this])},tHoz:function(A,B){B=B||this.axes.x;return(A-B.min)*B.scale},tVert:function(B,A){A=A||this.axes.y;return this.plotHeight-(B-A.min)*A.scale},drawGrid:function(){var B,E=this.options,A=this.ctx;if(E.grid.verticalLines||E.grid.horizontalLines){this.el.fire("flotr:beforegrid",[this.axes.x,this.axes.y,E,this])}A.save();A.translate(this.plotOffset.left,this.plotOffset.top);if(E.grid.backgroundColor!=null){A.fillStyle=E.grid.backgroundColor;A.fillRect(0,0,this.plotWidth,this.plotHeight)}A.lineWidth=1;A.strokeStyle=E.grid.tickColor;A.beginPath();if(E.grid.verticalLines){for(var D=0;D<this.axes.x.ticks.length;++D){B=this.axes.x.ticks[D].v;if((B==this.axes.x.min||B==this.axes.x.max)&&E.grid.outlineWidth!=0){continue}A.moveTo(Math.floor(this.tHoz(B))+A.lineWidth/2,0);A.lineTo(Math.floor(this.tHoz(B))+A.lineWidth/2,this.plotHeight)}}if(E.grid.horizontalLines){for(var C=0;C<this.axes.y.ticks.length;++C){B=this.axes.y.ticks[C].v;if((B==this.axes.y.min||B==this.axes.y.max)&&E.grid.outlineWidth!=0){continue}A.moveTo(0,Math.floor(this.tVert(B))+A.lineWidth/2);A.lineTo(this.plotWidth,Math.floor(this.tVert(B))+A.lineWidth/2)}}A.stroke();if(E.grid.outlineWidth!=0){A.lineWidth=E.grid.outlineWidth;A.strokeStyle=E.grid.color;A.lineJoin="round";A.strokeRect(0,0,this.plotWidth,this.plotHeight)}A.restore();if(E.grid.verticalLines||E.grid.horizontalLines){this.el.fire("flotr:aftergrid",[this.axes.x,this.axes.y,E,this])}},drawLabels:function(){var C=0,D,B,E,F,G,J=this.options,I=this.ctx,H=this.axes;for(E=0;E<H.x.ticks.length;++E){if(H.x.ticks[E].label){++C}}B=this.plotWidth/C;if(!J.HtmlText&&this.textEnabled){var A={size:J.fontSize,adjustAlign:true};D=H.x;A.color=D.options.color||J.grid.color;for(E=0;E<D.ticks.length&&D.options.showLabels&&D.used;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}A.angle=Flotr.toRad(D.options.labelsAngle);A.halign="c";A.valign="t";I.drawText(G.label,this.plotOffset.left+this.tHoz(G.v,D),this.plotOffset.top+this.plotHeight+J.grid.labelMargin,A)}D=H.x2;A.color=D.options.color||J.grid.color;for(E=0;E<D.ticks.length&&D.options.showLabels&&D.used;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}A.angle=Flotr.toRad(D.options.labelsAngle);A.halign="c";A.valign="b";I.drawText(G.label,this.plotOffset.left+this.tHoz(G.v,D),this.plotOffset.top+J.grid.labelMargin,A)}D=H.y;A.color=D.options.color||J.grid.color;for(E=0;E<D.ticks.length&&D.options.showLabels&&D.used;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}A.angle=Flotr.toRad(D.options.labelsAngle);A.halign="r";A.valign="m";I.drawText(G.label,this.plotOffset.left-J.grid.labelMargin,this.plotOffset.top+this.tVert(G.v,D),A)}D=H.y2;A.color=D.options.color||J.grid.color;for(E=0;E<D.ticks.length&&D.options.showLabels&&D.used;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}A.angle=Flotr.toRad(D.options.labelsAngle);A.halign="l";A.valign="m";I.drawText(G.label,this.plotOffset.left+this.plotWidth+J.grid.labelMargin,this.plotOffset.top+this.tVert(G.v,D),A);I.save();I.strokeStyle=A.color;I.beginPath();I.moveTo(this.plotOffset.left+this.plotWidth-8,this.plotOffset.top+this.tVert(G.v,D));I.lineTo(this.plotOffset.left+this.plotWidth,this.plotOffset.top+this.tVert(G.v,D));I.stroke();I.restore()}}else{if(H.x.options.showLabels||H.x2.options.showLabels||H.y.options.showLabels||H.y2.options.showLabels){F=['<div style="font-size:smaller;color:'+J.grid.color+';" class="flotr-labels">'];D=H.x;if(D.options.showLabels){for(E=0;E<D.ticks.length;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}F.push('<div style="position:absolute;top:'+(this.plotOffset.top+this.plotHeight+J.grid.labelMargin)+"px;left:"+(this.plotOffset.left+this.tHoz(G.v,D)-B/2)+"px;width:"+B+"px;text-align:center;"+(D.options.color?("color:"+D.options.color+";"):"")+'" class="flotr-grid-label">'+G.label+"</div>")}}D=H.x2;if(D.options.showLabels&&D.used){for(E=0;E<D.ticks.length;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}F.push('<div style="position:absolute;top:'+(this.plotOffset.top-J.grid.labelMargin-D.maxLabel.height)+"px;left:"+(this.plotOffset.left+this.tHoz(G.v,D)-B/2)+"px;width:"+B+"px;text-align:center;"+(D.options.color?("color:"+D.options.color+";"):"")+'" class="flotr-grid-label">'+G.label+"</div>")}}D=H.y;if(D.options.showLabels){for(E=0;E<D.ticks.length;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}F.push('<div style="position:absolute;top:'+(this.plotOffset.top+this.tVert(G.v,D)-D.maxLabel.height/2)+"px;left:0;width:"+(this.plotOffset.left-J.grid.labelMargin)+"px;text-align:right;"+(D.options.color?("color:"+D.options.color+";"):"")+'" class="flotr-grid-label">'+G.label+"</div>")}}D=H.y2;if(D.options.showLabels&&D.used){I.save();I.strokeStyle=D.options.color||J.grid.color;I.beginPath();for(E=0;E<D.ticks.length;++E){G=D.ticks[E];if(!G.label||G.label.length==0){continue}F.push('<div style="position:absolute;top:'+(this.plotOffset.top+this.tVert(G.v,D)-D.maxLabel.height/2)+"px;right:0;width:"+(this.plotOffset.right-J.grid.labelMargin)+"px;text-align:left;"+(D.options.color?("color:"+D.options.color+";"):"")+'" class="flotr-grid-label">'+G.label+"</div>");I.moveTo(this.plotOffset.left+this.plotWidth-8,this.plotOffset.top+this.tVert(G.v,D));I.lineTo(this.plotOffset.left+this.plotWidth,this.plotOffset.top+this.tVert(G.v,D))}I.stroke();I.restore()}F.push("</div>");this.el.insert(F.join(""))}}},drawTitles:function(){var D,C=this.options,F=C.grid.labelMargin,B=this.ctx,A=this.axes;if(!C.HtmlText&&this.textEnabled){var E={size:C.fontSize,color:C.grid.color,halign:"c"};if(C.subtitle){B.drawText(C.subtitle,this.plotOffset.left+this.plotWidth/2,this.titleHeight+this.subtitleHeight-2,E)}E.weight=1.5;E.size*=1.5;if(C.title){B.drawText(C.title,this.plotOffset.left+this.plotWidth/2,this.titleHeight-2,E)}E.weight=1.8;E.size*=0.8;E.adjustAlign=true;if(A.x.options.title&&A.x.used){E.halign="c";E.valign="t";E.angle=Flotr.toRad(A.x.options.titleAngle);B.drawText(A.x.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top+A.x.maxLabel.height+this.plotHeight+2*F,E)}if(A.x2.options.title&&A.x2.used){E.halign="c";E.valign="b";E.angle=Flotr.toRad(A.x2.options.titleAngle);B.drawText(A.x2.options.title,this.plotOffset.left+this.plotWidth/2,this.plotOffset.top-A.x2.maxLabel.height-2*F,E)}if(A.y.options.title&&A.y.used){E.halign="r";E.valign="m";E.angle=Flotr.toRad(A.y.options.titleAngle);B.drawText(A.y.options.title,this.plotOffset.left-A.y.maxLabel.width-2*F,this.plotOffset.top+this.plotHeight/2,E)}if(A.y2.options.title&&A.y2.used){E.halign="l";E.valign="m";E.angle=Flotr.toRad(A.y2.options.titleAngle);B.drawText(A.y2.options.title,this.plotOffset.left+this.plotWidth+A.y2.maxLabel.width+2*F,this.plotOffset.top+this.plotHeight/2,E)}}else{D=['<div style="color:'+C.grid.color+';" class="flotr-titles">'];if(C.title){D.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">'+C.title+"</div>")}if(C.subtitle){D.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">'+C.subtitle+"</div>")}D.push("</div>");D.push('<div class="flotr-axis-title" style="font-weight:bold;">');if(A.x.options.title&&A.x.used){D.push('<div style="position:absolute;top:'+(this.plotOffset.top+this.plotHeight+C.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>")}if(A.x2.options.title&&A.x2.used){D.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>")}if(A.y.options.title&&A.y.used){D.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>")}if(A.y2.options.title&&A.y2.used){D.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>")}D.push("</div>");this.el.insert(D.join(""))}},drawSeries:function(A){A=A||this.series;var C=false;for(var B in Flotr._registeredTypes){if(A[B]&&A[B].show){this[Flotr._registeredTypes[B]](A);C=true}}if(!C){this[Flotr._registeredTypes[this.options.defaultType]](A)}},plotLine:function(I,F){var O=this.ctx,A=I.xaxis,K=I.yaxis,J=this.tHoz.bind(this),M=this.tVert.bind(this),H=I.data;if(H.length<2){return }var E=J(H[0][0],A),D=M(H[0][1],K)+F;O.beginPath();O.moveTo(E,D);for(var G=0;G<H.length-1;++G){var C=H[G][0],N=H[G][1],B=H[G+1][0],L=H[G+1][1];if(N===null||L===null){continue}if(N<=L&&N<K.min){if(L<K.min){continue}C=(K.min-N)/(L-N)*(B-C)+C;N=K.min}else{if(L<=N&&L<K.min){if(N<K.min){continue}B=(K.min-N)/(L-N)*(B-C)+C;L=K.min}}if(N>=L&&N>K.max){if(L>K.max){continue}C=(K.max-N)/(L-N)*(B-C)+C;N=K.max}else{if(L>=N&&L>K.max){if(N>K.max){continue}B=(K.max-N)/(L-N)*(B-C)+C;L=K.max}}if(C<=B&&C<A.min){if(B<A.min){continue}N=(A.min-C)/(B-C)*(L-N)+N;C=A.min}else{if(B<=C&&B<A.min){if(C<A.min){continue}L=(A.min-C)/(B-C)*(L-N)+N;B=A.min}}if(C>=B&&C>A.max){if(B>A.max){continue}N=(A.max-C)/(B-C)*(L-N)+N;C=A.max}else{if(B>=C&&B>A.max){if(C>A.max){continue}L=(A.max-C)/(B-C)*(L-N)+N;B=A.max}}if(E!=J(C,A)||D!=M(N,K)+F){O.moveTo(J(C,A),M(N,K)+F)}E=J(B,A);D=M(L,K)+F;O.lineTo(E,D)}O.stroke()},plotLineArea:function(J,D){var S=J.data;if(S.length<2){return }var L,G=0,N=this.ctx,Q=J.xaxis,B=J.yaxis,E=this.tHoz.bind(this),M=this.tVert.bind(this),H=Math.min(Math.max(0,B.min),B.max),F=true;N.beginPath();for(var O=0;O<S.length-1;++O){var R=S[O][0],C=S[O][1],P=S[O+1][0],A=S[O+1][1];if(R<=P&&R<Q.min){if(P<Q.min){continue}C=(Q.min-R)/(P-R)*(A-C)+C;R=Q.min}else{if(P<=R&&P<Q.min){if(R<Q.min){continue}A=(Q.min-R)/(P-R)*(A-C)+C;P=Q.min}}if(R>=P&&R>Q.max){if(P>Q.max){continue}C=(Q.max-R)/(P-R)*(A-C)+C;R=Q.max}else{if(P>=R&&P>Q.max){if(R>Q.max){continue}A=(Q.max-R)/(P-R)*(A-C)+C;P=Q.max}}if(F){N.moveTo(E(R,Q),M(H,B)+D);F=false}if(C>=B.max&&A>=B.max){N.lineTo(E(R,Q),M(B.max,B)+D);N.lineTo(E(P,Q),M(B.max,B)+D);continue}else{if(C<=B.min&&A<=B.min){N.lineTo(E(R,Q),M(B.min,B)+D);N.lineTo(E(P,Q),M(B.min,B)+D);continue}}var I=R,K=P;if(C<=A&&C<B.min&&A>=B.min){R=(B.min-C)/(A-C)*(P-R)+R;C=B.min}else{if(A<=C&&A<B.min&&C>=B.min){P=(B.min-C)/(A-C)*(P-R)+R;A=B.min}}if(C>=A&&C>B.max&&A<=B.max){R=(B.max-C)/(A-C)*(P-R)+R;C=B.max}else{if(A>=C&&A>B.max&&C<=B.max){P=(B.max-C)/(A-C)*(P-R)+R;A=B.max}}if(R!=I){L=(C<=B.min)?L=B.min:B.max;N.lineTo(E(I,Q),M(L,B)+D);N.lineTo(E(R,Q),M(L,B)+D)}N.lineTo(E(R,Q),M(C,B)+D);N.lineTo(E(P,Q),M(A,B)+D);if(P!=K){L=(A<=B.min)?B.min:B.max;N.lineTo(E(K,Q),M(L,B)+D);N.lineTo(E(P,Q),M(L,B)+D)}G=Math.max(P,K)}N.lineTo(E(G,Q),M(H,B)+D);N.closePath();N.fill()},drawSeriesLines:function(C){C=C||this.series;var B=this.ctx;B.save();B.translate(this.plotOffset.left,this.plotOffset.top);B.lineJoin="round";var D=C.lines.lineWidth;var A=C.shadowSize;if(A>0){B.lineWidth=A/2;var E=D/2+B.lineWidth/2;B.strokeStyle="rgba(0,0,0,0.1)";this.plotLine(C,E+A/2);B.strokeStyle="rgba(0,0,0,0.2)";this.plotLine(C,E);if(C.lines.fill){B.fillStyle="rgba(0,0,0,0.05)";this.plotLineArea(C,E+A/2)}}B.lineWidth=D;B.strokeStyle=C.color;if(C.lines.fill){B.fillStyle=C.lines.fillColor!=null?C.lines.fillColor:Flotr.parseColor(C.color).scale(null,null,null,C.lines.fillOpacity).toString();this.plotLineArea(C,0)}this.plotLine(C,0);B.restore()},drawSeriesPoints:function(C){var B=this.ctx;B.save();B.translate(this.plotOffset.left,this.plotOffset.top);var D=C.lines.lineWidth;var A=C.shadowSize;if(A>0){B.lineWidth=A/2;B.strokeStyle="rgba(0,0,0,0.1)";this.plotPointShadows(C,A/2+B.lineWidth/2,C.points.radius);B.strokeStyle="rgba(0,0,0,0.2)";this.plotPointShadows(C,B.lineWidth/2,C.points.radius)}B.lineWidth=C.points.lineWidth;B.strokeStyle=C.color;B.fillStyle=C.points.fillColor!=null?C.points.fillColor:C.color;this.plotPoints(C,C.points.radius,C.points.fill);B.restore()},plotPoints:function(C,E,I){var A=C.xaxis,F=C.yaxis,J=this.ctx,D,B=C.data;for(D=B.length-1;D>-1;--D){var H=B[D][0],G=B[D][1];if(H<A.min||H>A.max||G<F.min||G>F.max){continue}J.beginPath();J.arc(this.tHoz(H,A),this.tVert(G,F),E,0,2*Math.PI,true);if(I){J.fill()}J.stroke()}},plotPointShadows:function(D,B,F){var A=D.xaxis,G=D.yaxis,J=this.ctx,E,C=D.data;for(E=C.length-1;E>-1;--E){var I=C[E][0],H=C[E][1];if(I<A.min||I>A.max||H<G.min||H>G.max){continue}J.beginPath();J.arc(this.tHoz(I,A),this.tVert(H,G)+B,F,0,Math.PI,false);J.stroke()}},drawSeriesBars:function(B){var A=this.ctx,D=B.bars.barWidth,C=Math.min(B.bars.lineWidth,D);A.save();A.translate(this.plotOffset.left,this.plotOffset.top);A.lineJoin="miter";A.lineWidth=C;A.strokeStyle=B.color;this.plotBarsShadows(B,D,0,B.bars.fill);if(B.bars.fill){A.fillStyle=B.bars.fillColor!=null?B.bars.fillColor:Flotr.parseColor(B.color).scale(null,null,null,B.bars.fillOpacity).toString()}this.plotBars(B,D,0,B.bars.fill);A.restore()},plotBars:function(K,N,D,Q){var U=K.data;if(U.length<1){return }var S=K.xaxis,B=K.yaxis,P=this.ctx,F=this.tHoz.bind(this),O=this.tVert.bind(this);for(var R=0;R<U.length;R++){var J=U[R][0],I=U[R][1];var E=true,L=true,A=true;var H=0;if(K.bars.stacked){S.values.each(function(W,V){if(V==J){H=W.stack||0;W.stack=H+I}})}if(K.bars.horizontal){var C=H,T=J+H,G=I,M=I+N}else{var C=J,T=J+N,G=H,M=I+H}if(T<S.min||C>S.max||M<B.min||G>B.max){continue}if(C<S.min){C=S.min;E=false}if(T>S.max){T=S.max;if(S.lastSerie!=K&&K.bars.horizontal){L=false}}if(G<B.min){G=B.min}if(M>B.max){M=B.max;if(B.lastSerie!=K&&!K.bars.horizontal){L=false}}if(Q){P.beginPath();P.moveTo(F(C,S),O(G,B)+D);P.lineTo(F(C,S),O(M,B)+D);P.lineTo(F(T,S),O(M,B)+D);P.lineTo(F(T,S),O(G,B)+D);P.fill()}if(K.bars.lineWidth!=0&&(E||A||L)){P.beginPath();P.moveTo(F(C,S),O(G,B)+D);P[E?"lineTo":"moveTo"](F(C,S),O(M,B)+D);P[L?"lineTo":"moveTo"](F(T,S),O(M,B)+D);P[A?"lineTo":"moveTo"](F(T,S),O(G,B)+D);P.stroke()}}},plotBarsShadows:function(I,K,C){var T=I.data;if(T.length<1){return }var R=I.xaxis,A=I.yaxis,P=this.ctx,D=this.tHoz.bind(this),M=this.tVert.bind(this),N=this.options.shadowSize;for(var Q=0;Q<T.length;Q++){var H=T[Q][0],G=T[Q][1];var E=0;if(I.bars.stacked){R.values.each(function(V,U){if(U==H){E=V.stackShadow||0;V.stackShadow=E+G}})}if(I.bars.horizontal){var B=E,S=H+E,F=G,J=G+K}else{var B=H,S=H+K,F=E,J=G+E}if(S<R.min||B>R.max||J<A.min||F>A.max){continue}if(B<R.min){B=R.min}if(S>R.max){S=R.max}if(F<A.min){F=A.min}if(J>A.max){J=A.max}var O=D(S,R)-D(B,R)-((D(S,R)+N<=this.plotWidth)?0:N);var L=Math.max(0,M(F,A)-M(J,A)-((M(F,A)+N<=this.plotHeight)?0:N));P.fillStyle="rgba(0,0,0,0.05)";P.fillRect(Math.min(D(B,R)+N,this.plotWidth),Math.min(M(J,A)+N,this.plotWidth),O,L)}},drawSeriesCandles:function(B){var A=this.ctx,C=B.candles.candleWidth;A.save();A.translate(this.plotOffset.left,this.plotOffset.top);A.lineJoin="miter";A.lineWidth=B.candles.lineWidth;this.plotCandlesShadows(B,C/2);this.plotCandles(B,C/2);A.restore()},plotCandles:function(K,D){var W=K.data;if(W.length<1){return }var T=K.xaxis,B=K.yaxis,P=this.ctx,E=this.tHoz.bind(this),O=this.tVert.bind(this);for(var S=0;S<W.length;S++){var U=W[S],J=U[0],L=U[1],I=U[2],X=U[3],N=U[4];var C=J,V=J+K.candles.candleWidth,G=Math.max(B.min,X),M=Math.min(B.max,I),A=Math.max(B.min,Math.min(L,N)),R=Math.min(B.max,Math.max(L,N));if(V<T.min||C>T.max||M<B.min||G>B.max){continue}var Q=K.candles[L>N?"downFillColor":"upFillColor"];if(K.candles.fill&&!K.candles.barcharts){P.fillStyle=Flotr.parseColor(Q).scale(null,null,null,K.candles.fillOpacity).toString();P.fillRect(E(C,T),O(R,B)+D,E(V,T)-E(C,T),O(A,B)-O(R,B))}if(K.candles.lineWidth||K.candles.wickLineWidth){var J,H,F=(K.candles.wickLineWidth%2)/2;J=Math.floor(E((C+V)/2),T)+F;P.save();P.strokeStyle=Q;P.lineWidth=K.candles.wickLineWidth;P.lineCap="butt";if(K.candles.barcharts){P.beginPath();P.moveTo(J,Math.floor(O(M,B)+D));P.lineTo(J,Math.floor(O(G,B)+D));H=Math.floor(O(L,B)+D)+0.5;P.moveTo(Math.floor(E(C,T))+F,H);P.lineTo(J,H);H=Math.floor(O(N,B)+D)+0.5;P.moveTo(Math.floor(E(V,T))+F,H);P.lineTo(J,H)}else{P.strokeRect(E(C,T),O(R,B)+D,E(V,T)-E(C,T),O(A,B)-O(R,B));P.beginPath();P.moveTo(J,Math.floor(O(R,B)+D));P.lineTo(J,Math.floor(O(M,B)+D));P.moveTo(J,Math.floor(O(A,B)+D));P.lineTo(J,Math.floor(O(G,B)+D))}P.stroke();P.restore()}}},plotCandlesShadows:function(H,C){var T=H.data;if(T.length<1||H.candles.barcharts){return }var Q=H.xaxis,A=H.yaxis,D=this.tHoz.bind(this),M=this.tVert.bind(this),N=this.options.shadowSize;for(var P=0;P<T.length;P++){var R=T[P],G=R[0],I=R[1],F=R[2],U=R[3],K=R[4];var B=G,S=G+H.candles.candleWidth,E=Math.max(A.min,Math.min(I,K)),J=Math.min(A.max,Math.max(I,K));if(S<Q.min||B>Q.max||J<A.min||E>A.max){continue}var O=D(S,Q)-D(B,Q)-((D(S,Q)+N<=this.plotWidth)?0:N);var L=Math.max(0,M(E,A)-M(J,A)-((M(E,A)+N<=this.plotHeight)?0:N));this.ctx.fillStyle="rgba(0,0,0,0.05)";this.ctx.fillRect(Math.min(D(B,Q)+N,this.plotWidth),Math.min(M(J,A)+N,this.plotWidth),O,L)}},drawSeriesPie:function(G){if(!this.options.pie.drawn){var K=this.ctx,C=this.options,E=G.pie.lineWidth,I=G.shadowSize,R=G.data,D=(Math.min(this.canvasWidth,this.canvasHeight)*G.pie.sizeRatio)/2,H=[];var L=1;var P=Math.sin(G.pie.viewAngle)*G.pie.spliceThickness/L;var M={size:C.fontSize*1.2,color:C.grid.color,weight:1.5};var Q={x:(this.canvasWidth+this.plotOffset.left)/2,y:(this.canvasHeight-this.plotOffset.bottom)/2};var O=this.series.collect(function(T,S){if(T.pie.show){return{name:(T.label||T.data[0][1]),value:[S,T.data[0][1]],explode:T.pie.explode}}});var B=O.pluck("value").pluck(1).inject(0,function(S,T){return S+T});var F=0,N=G.pie.startAngle,J=0;var A=O.collect(function(S){N+=F;J=parseFloat(S.value[1]);F=J/B;return{name:S.name,fraction:F,x:S.value[0],y:J,explode:S.explode,startAngle:2*N*Math.PI,endAngle:2*(N+F)*Math.PI}});K.save();if(I>0){A.each(function(V){var S=(V.startAngle+V.endAngle)/2;var T=Q.x+Math.cos(S)*V.explode+I;var U=Q.y+Math.sin(S)*V.explode+I;this.plotSlice(T,U,D,V.startAngle,V.endAngle,false,L);K.fillStyle="rgba(0,0,0,0.1)";K.fill()},this)}if(C.HtmlText){H=['<div style="color:'+this.options.grid.color+'" class="flotr-labels">']}A.each(function(c,X){var W=(c.startAngle+c.endAngle)/2;var V=C.colors[X];var Y=Q.x+Math.cos(W)*c.explode;var U=Q.y+Math.sin(W)*c.explode;this.plotSlice(Y,U,D,c.startAngle,c.endAngle,false,L);if(G.pie.fill){K.fillStyle=Flotr.parseColor(V).scale(null,null,null,G.pie.fillOpacity).toString();K.fill()}K.lineWidth=E;K.strokeStyle=V;K.stroke();var b=C.pie.labelFormatter(c);var S=(Math.cos(W)<0);var a=Y+Math.cos(W)*(G.pie.explode+D);var Z=U+Math.sin(W)*(G.pie.explode+D);if(c.fraction&&b){if(C.HtmlText){var T="position:absolute;top:"+(Z-5)+"px;";if(S){T+="right:"+(this.canvasWidth-a)+"px;text-align:right;"}else{T+="left:"+a+"px;text-align:left;"}H.push('<div style="'+T+'" class="flotr-grid-label">'+b+"</div>")}else{M.halign=S?"r":"l";K.drawText(b,a,Z+M.size/2,M)}}},this);if(C.HtmlText){H.push("</div>");this.el.insert(H.join(""))}K.restore();C.pie.drawn=true}},plotSlice:function(B,H,A,E,D,F,G){var C=this.ctx;G=G||1;C.save();C.scale(1,G);C.beginPath();C.moveTo(B,H);C.arc(B,H,A,E,D,F);C.lineTo(B,H);C.closePath();C.restore()},plotPie:function(){},insertLegend:function(){if(!this.options.legend.show){return }var H=this.series,I=this.plotOffset,B=this.options,b=[],A=false,O=this.ctx,R;var Q=H.findAll(function(c){return(c.label&&!c.hide)}).size();if(Q){if(!B.HtmlText&&this.textEnabled){var T={size:B.fontSize*1.1,color:B.grid.color};var M=B.legend.position,N=B.legend.margin,L=B.legend.labelBoxWidth,Z=B.legend.labelBoxHeight,S=B.legend.labelBoxMargin,W=I.left+N,U=I.top+N;var a=0;for(R=H.length-1;R>-1;--R){if(!H[R].label||H[R].hide){continue}var E=B.legend.labelFormatter(H[R].label);a=Math.max(a,O.measureText(E,T))}var K=Math.round(L+S*3+a),C=Math.round(Q*(S+Z)+S);if(M.charAt(0)=="s"){U=I.top+this.plotHeight-(N+C)}if(M.charAt(1)=="e"){W=I.left+this.plotWidth-(N+K)}var P=Flotr.parseColor(B.legend.backgroundColor||"rgb(240,240,240)").scale(null,null,null,B.legend.backgroundOpacity||0.1).toString();O.fillStyle=P;O.fillRect(W,U,K,C);O.strokeStyle=B.legend.labelBoxBorderColor;O.strokeRect(Flotr.toPixel(W),Flotr.toPixel(U),K,C);var G=W+S;var F=U+S;for(R=0;R<H.length;R++){if(!H[R].label||H[R].hide){continue}var E=B.legend.labelFormatter(H[R].label);O.fillStyle=H[R].color;O.fillRect(G,F,L-1,Z-1);O.strokeStyle=B.legend.labelBoxBorderColor;O.lineWidth=1;O.strokeRect(Math.ceil(G)-1.5,Math.ceil(F)-1.5,L+2,Z+2);O.drawText(E,G+L+S,F+(Z+T.size-O.fontDescent(T))/2,T);F+=Z+S}}else{for(R=0;R<H.length;++R){if(!H[R].label||H[R].hide){continue}if(R%B.legend.noColumns==0){b.push(A?"</tr><tr>":"<tr>");A=true}var E=B.legend.labelFormatter(H[R].label);b.push('<td class="flotr-legend-color-box"><div style="border:1px solid '+B.legend.labelBoxBorderColor+';padding:1px"><div style="width:'+B.legend.labelBoxWidth+"px;height:"+B.legend.labelBoxHeight+"px;background-color:"+H[R].color+'"></div></div></td><td class="flotr-legend-label">'+E+"</td>")}if(A){b.push("</tr>")}if(b.length>0){var V='<table style="font-size:smaller;color:'+B.grid.color+'">'+b.join("")+"</table>";if(B.legend.container!=null){$(B.legend.container).update(V)}else{var D="";var M=B.legend.position,N=B.legend.margin;if(M.charAt(0)=="n"){D+="top:"+(N+I.top)+"px;"}else{if(M.charAt(0)=="s"){D+="bottom:"+(N+I.bottom)+"px;"}}if(M.charAt(1)=="e"){D+="right:"+(N+I.right)+"px;"}else{if(M.charAt(1)=="w"){D+="left:"+(N+I.left)+"px;"}}var J=this.el.insert('<div class="flotr-legend" style="position:absolute;z-index:2;'+D+'">'+V+"</div>").select("div.flotr-legend").first();if(B.legend.backgroundOpacity!=0){var Y=B.legend.backgroundColor;if(Y==null){var X=(B.grid.backgroundColor!=null)?B.grid.backgroundColor:Flotr.extractColor(J);Y=Flotr.parseColor(X).adjust(null,null,null,1).toString()}this.el.insert('<div class="flotr-legend-bg" style="position:absolute;width:'+J.getWidth()+"px;height:"+J.getHeight()+"px;"+D+"background-color:"+Y+';"> </div>').select("div.flotr-legend-bg").first().setStyle({opacity:B.legend.backgroundOpacity})}}}}}},getEventPosition:function(C){var G=this.overlay.cumulativeOffset(),F=(C.pageX-G.left-this.plotOffset.left),E=(C.pageY-G.top-this.plotOffset.top),D=0,B=0;if(C.pageX==null&&C.clientX!=null){var H=document.documentElement,A=document.body;D=C.clientX+(H&&H.scrollLeft||A.scrollLeft||0);B=C.clientY+(H&&H.scrollTop||A.scrollTop||0)}else{D=C.pageX;B=C.pageY}return{x:this.axes.x.min+F/this.axes.x.scale,x2:this.axes.x2.min+F/this.axes.x2.scale,y:this.axes.y.max-E/this.axes.y.scale,y2:this.axes.y2.max-E/this.axes.y2.scale,relX:F,relY:E,absX:D,absY:B}},clickHandler:function(A){if(this.ignoreClick){this.ignoreClick=false;return }this.el.fire("flotr:click",[this.getEventPosition(A),this])},mouseMoveHandler:function(A){var B=this.getEventPosition(A);this.lastMousePos.pageX=B.absX;this.lastMousePos.pageY=B.absY;if(this.selectionInterval==null&&(this.options.mouse.track||this.series.any(function(C){return C.mouse&&C.mouse.track}))){this.hit(B)}this.el.fire("flotr:mousemove",[A,B,this])},mouseDownHandler:function(C){if(C.isRightClick()){C.stop();var B=this.overlay;B.hide();function A(){B.show();$(document).stopObserving("mousemove",A)}$(document).observe("mousemove",A);return }if(!this.options.selection.mode||!C.isLeftClick()){return }this.setSelectionPos(this.selection.first,C);if(this.selectionInterval!=null){clearInterval(this.selectionInterval)}this.lastMousePos.pageX=null;this.selectionInterval=setInterval(this.updateSelection.bind(this),1000/this.options.selection.fps);this.mouseUpHandler=this.mouseUpHandler.bind(this);$(document).observe("mouseup",this.mouseUpHandler)},fireSelectEvent:function(){var A=this.axes,F=this.selection,C=(F.first.x<=F.second.x)?F.first.x:F.second.x,B=(F.first.x<=F.second.x)?F.second.x:F.first.x,E=(F.first.y>=F.second.y)?F.first.y:F.second.y,D=(F.first.y>=F.second.y)?F.second.y:F.first.y;C=A.x.min+C/A.x.scale;B=A.x.min+B/A.x.scale;E=A.y.max-E/A.y.scale;D=A.y.max-D/A.y.scale;this.el.fire("flotr:select",[{x1:C,y1:E,x2:B,y2:D},this])},mouseUpHandler:function(A){$(document).stopObserving("mouseup",this.mouseUpHandler);A.stop();if(this.selectionInterval!=null){clearInterval(this.selectionInterval);this.selectionInterval=null}this.setSelectionPos(this.selection.second,A);this.clearSelection();if(this.selectionIsSane()){this.drawSelection();this.fireSelectEvent();this.ignoreClick=true}},setSelectionPos:function(D,B){var A=this.options,C=$(this.overlay).cumulativeOffset();if(A.selection.mode.indexOf("x")==-1){D.x=(D==this.selection.first)?0:this.plotWidth}else{D.x=B.pageX-C.left-this.plotOffset.left;D.x=Math.min(Math.max(0,D.x),this.plotWidth)}if(A.selection.mode.indexOf("y")==-1){D.y=(D==this.selection.first)?0:this.plotHeight}else{D.y=B.pageY-C.top-this.plotOffset.top;D.y=Math.min(Math.max(0,D.y),this.plotHeight)}},updateSelection:function(){if(this.lastMousePos.pageX==null){return }this.setSelectionPos(this.selection.second,this.lastMousePos);this.clearSelection();if(this.selectionIsSane()){this.drawSelection()}},clearSelection:function(){if(this.prevSelection==null){return }var G=this.prevSelection,E=this.octx,C=this.plotOffset,A=Math.min(G.first.x,G.second.x),F=Math.min(G.first.y,G.second.y),B=Math.abs(G.second.x-G.first.x),D=Math.abs(G.second.y-G.first.y);E.clearRect(A+C.left-E.lineWidth,F+C.top-E.lineWidth,B+E.lineWidth*2,D+E.lineWidth*2);this.prevSelection=null},setSelection:function(G){var B=this.options,H=this.axes.x,A=this.axes.y,F=yaxis.scale,D=xaxis.scale,E=B.selection.mode.indexOf("x")!=-1,C=B.selection.mode.indexOf("y")!=-1;this.clearSelection();this.selection.first.y=E?0:(A.max-G.y1)*F;this.selection.second.y=E?this.plotHeight:(A.max-G.y2)*F;this.selection.first.x=C?0:(G.x1-H.min)*D;this.selection.second.x=C?this.plotWidth:(G.x2-H.min)*D;this.drawSelection();this.fireSelectEvent()},drawSelection:function(){var C=this.prevSelection,F=this.selection,H=this.octx,I=this.options,A=this.plotOffset;if(C!=null&&F.first.x==C.first.x&&F.first.y==C.first.y&&F.second.x==C.second.x&&F.second.y==C.second.y){return }H.strokeStyle=Flotr.parseColor(I.selection.color).scale(null,null,null,0.8).toString();H.lineWidth=1;H.lineJoin="round";H.fillStyle=Flotr.parseColor(I.selection.color).scale(null,null,null,0.4).toString();this.prevSelection={first:{x:F.first.x,y:F.first.y},second:{x:F.second.x,y:F.second.y}};var E=Math.min(F.first.x,F.second.x),D=Math.min(F.first.y,F.second.y),G=Math.abs(F.second.x-F.first.x),B=Math.abs(F.second.y-F.first.y);H.fillRect(E+A.left,D+A.top,G,B);H.strokeRect(E+A.left,D+A.top,G,B)},selectionIsSane:function(){var A=this.selection;return Math.abs(A.second.x-A.first.x)>=5&&Math.abs(A.second.y-A.first.y)>=5},clearHit:function(){if(this.prevHit){var B=this.options,A=this.plotOffset,C=this.prevHit;this.octx.clearRect(this.tHoz(C.x)+A.left-B.points.radius*2,this.tVert(C.y)+A.top-B.points.radius*2,B.points.radius*3+B.points.lineWidth*3,B.points.radius*3+B.points.lineWidth*3);this.prevHit=null}},hit:function(I){var G=this.series,C=this.options,R=this.prevHit,H=this.plotOffset,D=this.octx,S,A,M,Q,L={dist:Number.MAX_VALUE,x:null,y:null,relX:I.relX,relY:I.relY,absX:I.absX,absY:I.absY,mouse:null};for(Q=0;Q<G.length;Q++){s=G[Q];if(!s.mouse.track){continue}S=s.data;A=(s.xaxis.scale*s.mouse.sensibility);M=(s.yaxis.scale*s.mouse.sensibility);for(var P=0,B,E;P<S.length;P++){if(S[P][1]===null){continue}B=Math.pow(s.xaxis.scale*(S[P][0]-I.x),2);E=Math.pow(s.yaxis.scale*(S[P][1]-I.y),2);if(B<A&&E<M&&Math.sqrt(B+E)<L.dist){L.dist=Math.sqrt(B+E);L.x=S[P][0];L.y=S[P][1];L.mouse=s.mouse}}}if(L.mouse&&L.mouse.track&&!R||(R&&(L.x!=R.x||L.y!=R.y))){var K=this.mouseTrack||this.el.select(".flotr-mouse-value")[0],F="",J=C.mouse.position,N=C.mouse.margin,O="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;";if(!C.mouse.relative){if(J.charAt(0)=="n"){F+="top:"+(N+H.top)+"px;"}else{if(J.charAt(0)=="s"){F+="bottom:"+(N+H.bottom)+"px;"}}if(J.charAt(1)=="e"){F+="right:"+(N+H.right)+"px;"}else{if(J.charAt(1)=="w"){F+="left:"+(N+H.left)+"px;"}}}else{if(J.charAt(0)=="n"){F+="bottom:"+(N-H.top-this.tVert(L.y)+this.canvasHeight)+"px;"}else{if(J.charAt(0)=="s"){F+="top:"+(N+H.top+this.tVert(L.y))+"px;"}}if(J.charAt(1)=="e"){F+="left:"+(N+H.left+this.tHoz(L.x))+"px;"}else{if(J.charAt(1)=="w"){F+="right:"+(N-H.left-this.tHoz(L.x)+this.canvasWidth)+"px;"}}}O+=F;if(!K){this.el.insert('<div class="flotr-mouse-value" style="'+O+'"></div>');K=this.mouseTrack=this.el.select(".flotr-mouse-value").first()}else{this.mouseTrack=K.setStyle(O)}if(L.x!==null&&L.y!==null){K.show();this.clearHit();if(L.mouse.lineColor!=null){D.save();D.translate(H.left,H.top);D.lineWidth=C.points.lineWidth;D.strokeStyle=L.mouse.lineColor;D.fillStyle="#ffffff";D.beginPath();D.arc(this.tHoz(L.x),this.tVert(L.y),C.mouse.radius,0,2*Math.PI,true);D.fill();D.stroke();D.restore()}this.prevHit=L;var T=L.mouse.trackDecimals;if(T==null||T<0){T=0}K.innerHTML=L.mouse.trackFormatter({x:L.x.toFixed(T),y:L.y.toFixed(T)});K.fire("flotr:hit",[L,this])}else{if(R){K.hide();this.clearHit()}}}},saveImage:function(D,C,A,B){var E=null;switch(D){case"jpeg":case"jpg":E=Canvas2Image.saveAsJPEG(this.canvas,B,C,A);break;default:case"png":E=Canvas2Image.saveAsPNG(this.canvas,B,C,A);break;case"bmp":E=Canvas2Image.saveAsBMP(this.canvas,B,C,A);break}if(Object.isElement(E)&&B){this.restoreCanvas();this.canvas.hide();this.overlay.hide();this.el.insert(E.setStyle({position:"absolute"}))}},restoreCanvas:function(){this.canvas.show();this.overlay.show();this.el.select("img").invoke("remove")}});Flotr.Color=Class.create({initialize:function(E,D,B,C){this.rgba=["r","g","b","a"];var A=4;while(-1<--A){this[this.rgba[A]]=arguments[A]||((A==3)?1:0)}this.normalize()},adjust:function(D,C,E,B){var A=4;while(-1<--A){if(arguments[A]!=null){this[this.rgba[A]]+=arguments[A]}}return this.normalize()},clone:function(){return new Flotr.Color(this.r,this.b,this.g,this.a)},limit:function(B,A,C){return Math.max(Math.min(B,C),A)},normalize:function(){var A=this.limit;this.r=A(parseInt(this.r),0,255);this.g=A(parseInt(this.g),0,255);this.b=A(parseInt(this.b),0,255);this.a=A(this.a,0,1);return this},scale:function(D,C,E,B){var A=4;while(-1<--A){if(arguments[A]!=null){this[this.rgba[A]]*=arguments[A]}}return this.normalize()},distance:function(B){if(!B){return }B=new Flotr.parseColor(B);var C=0;var A=3;while(-1<--A){C+=Math.abs(this[this.rgba[A]]-B[this.rgba[A]])}return C},toString:function(){return(this.a>=1)?"rgb("+[this.r,this.g,this.b].join(",")+")":"rgba("+[this.r,this.g,this.b,this.a].join(",")+")"}});Flotr.Color.lookupColors={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]};Flotr.Date={format:function(F,E){if(!F){return }var A=function(H){H=H.toString();return H.length==1?"0"+H:H};var D=[];var C=false;for(var B=0;B<E.length;++B){var G=E.charAt(B);if(C){switch(G){case"h":G=F.getUTCHours().toString();break;case"H":G=A(F.getUTCHours());break;case"M":G=A(F.getUTCMinutes());break;case"S":G=A(F.getUTCSeconds());break;case"d":G=F.getUTCDate().toString();break;case"m":G=(F.getUTCMonth()+1).toString();break;case"y":G=F.getUTCFullYear().toString();break;case"b":G=Flotr.Date.monthNames[F.getUTCMonth()];break}D.push(G);C=false}else{if(G=="%"){C=true}else{D.push(G)}}}return D.join("")},timeUnits:{second:1000,minute:60*1000,hour:60*60*1000,day:24*60*60*1000,month:30*24*60*60*1000,year:365.2425*24*60*60*1000},spec:[[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"]}; |
//Flotr 0.2.0-alpha Copyright (c) 2009 Bas Wenneker, <http://solutoire.com>, MIT License. | |
// | |
//Radar chart added by Ryan Simmons | |
// | |
/* $Id: flotr.js 82 2009-01-12 19:19:31Z fabien.menager $ */ | |
var Flotr = { | |
version: '0.2.0-alpha', | |
author: 'Bas Wenneker', | |
website: 'http://www.solutoire.com', | |
/** | |
* An object of the default registered graph types. Use Flotr.register(type, functionName) | |
* to add your own type. | |
*/ | |
_registeredTypes:{ | |
'lines': 'drawSeriesLines', | |
'points': 'drawSeriesPoints', | |
'bars': 'drawSeriesBars', | |
'candles': 'drawSeriesCandles', | |
'pie': 'drawSeriesPie', | |
'radar':'drawSeriesRadar' | |
}, | |
/** | |
* Can be used to register your own chart type. Default types are 'lines', 'points' and 'bars'. | |
* This is still experimental. | |
* @todo Test and confirm. | |
* @param {String} type - type of chart, like 'pies', 'bars' etc. | |
* @param {String} functionName - Name of the draw function, like 'drawSeriesPies', 'drawSeriesBars' etc. | |
*/ | |
register: function(type, functionName){ | |
Flotr._registeredTypes[type] = functionName+''; | |
}, | |
/** | |
* 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 {Class} 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); | |
}, | |
/** | |
* 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 data.collect(function(serie){ | |
var i, serie = (serie.data) ? Object.clone(serie) : {'data': serie}; | |
for (i = serie.data.length-1; i > -1; --i) { | |
serie.data[i][1] = (serie.data[i][1] === null ? null : parseFloat(serie.data[i][1])); | |
} | |
return serie; | |
}); | |
}, | |
/** | |
* 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 | |
*/ | |
merge: function(src, dest){ | |
var result = dest || {}; | |
for(var i in src){ | |
result[i] = (src[i] != null && typeof(src[i]) == 'object' && !(src[i].constructor == Array || src[i].constructor == RegExp) && !Object.isElement(src[i])) ? Flotr.merge(src[i], dest[i]) : result[i] = src[i]; | |
} | |
return result; | |
}, | |
/** | |
* 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; | |
var magn = Flotr.getMagnitude(delta); | |
// Norm is between 1.0 and 10.0. | |
var norm = delta / magn; | |
var tickSize = 10; | |
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 | |
* @return {String} formatted tick string | |
*/ | |
defaultTickFormatter: function(val){ | |
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+')'; | |
}, | |
defaultPieLabelFormatter: function(slice) { | |
return (slice.fraction*100).toFixed(2)+'%'; | |
}, | |
/** | |
* 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); | |
}, | |
/** | |
* Parses a color string and returns a corresponding Color. | |
* @param {String} str - string thats representing a color | |
* @return {Color} returns a Color object or false | |
*/ | |
parseColor: function(str){ | |
if (str instanceof Flotr.Color) return str; | |
var result, Color = Flotr.Color; | |
// 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(str))) | |
return new Color(parseInt(result[1]), parseInt(result[2]), parseInt(result[3])); | |
// 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(str))) | |
return new Color(parseInt(result[1]), parseInt(result[2]), parseInt(result[3]), 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(str))) | |
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(str))) | |
return new Color(parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55, parseFloat(result[4])); | |
// #a0b1c2 | |
if((result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))) | |
return new Color(parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)); | |
// #fff | |
if((result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))) | |
return new Color(parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)); | |
// Otherwise, we're most likely dealing with a named color. | |
var name = str.strip().toLowerCase(); | |
if(name == 'transparent'){ | |
return new Color(255, 255, 255, 0); | |
} | |
return ((result = Color.lookupColors[name])) ? new Color(result[0], result[1], result[2]) : false; | |
}, | |
/** | |
* Extracts the background-color of the passed element. | |
* @param {Element} element | |
* @return {String} color string | |
*/ | |
extractColor: function(element){ | |
var color; | |
// Loop until we find an element with a background color and stop when we hit the body element. | |
do { | |
color = element.getStyle('background-color').toLowerCase(); | |
if(!(color == '' || color == 'transparent')) break; | |
element = element.up(0); | |
} while(!element.nodeName.match(/^body$/i)); | |
// Catch Safari's way of signaling transparent. | |
return (color == 'rgba(0, 0, 0, 0)') ? 'transparent' : color; | |
} | |
}; | |
/** | |
* Flotr Graph class that plots a graph on creation. | |
*/ | |
Flotr.Graph = Class.create({ | |
/** | |
* 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 | |
*/ | |
initialize: function(el, data, options){ | |
this.el = $(el); | |
if (!this.el) throw 'The target container doesn\'t exist'; | |
this.data = data; | |
this.series = Flotr.getSeries(data); | |
this.setOptions(options); | |
// Initialize some variables | |
this.lastMousePos = { pageX: null, pageY: null }; | |
this.selection = { first: { x: -1, y: -1}, second: { x: -1, y: -1} }; | |
this.prevSelection = null; | |
this.selectionInterval = null; | |
this.ignoreClick = false; | |
this.prevHit = null; | |
// Create and prepare canvas. | |
this.constructCanvas(); | |
// Add event handlers for mouse tracking, clicking and selection | |
this.initEvents(); | |
this.findDataRanges(); | |
this.calculateTicks(this.axes.x); | |
this.calculateTicks(this.axes.x2); | |
this.calculateTicks(this.axes.y); | |
this.calculateTicks(this.axes.y2); | |
this.calculateSpacing(); | |
this.draw(); | |
this.insertLegend(); | |
// Graph and Data tabs | |
if (this.options.spreadsheet.show) | |
this.constructTabs(); | |
}, | |
/** | |
* Sets options and initializes some variables and color specific values, used by the constructor. | |
* @param {Object} opts - options object | |
*/ | |
setOptions: function(opts){ | |
var options = { | |
colors: ['#00A8F0', '#C0D800', '#CB4B4B', '#4DA74D', '#9440ED'], //=> The default colorscheme. When there are > 5 series, additional colors are generated. | |
title: null, | |
subtitle: null, | |
legend: { | |
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: Prototype.K, // => fn: string -> string | |
labelBoxBorderColor: '#CCCCCC', // => border color for the little label boxes | |
labelBoxWidth: 14, | |
labelBoxHeight: 10, | |
labelBoxMargin: 5, | |
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 | |
}, | |
xaxis: { | |
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3] | |
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise | |
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 | |
tickFormatter: Flotr.defaultTickFormatter, // => fn: number -> 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 | |
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max | |
color: null | |
}, | |
x2axis: {}, | |
yaxis: { | |
ticks: null, // => format: either [1, 3] or [[1, 'a'], 3] | |
showLabels: true, // => setting to true will show the axis ticks labels, hide otherwise | |
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 | |
tickFormatter: Flotr.defaultTickFormatter, // => fn: number -> 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 | |
autoscaleMargin: 0, // => margin in % to add if auto-setting min/max | |
color: null | |
}, | |
y2axis: { | |
titleAngle: 270 | |
}, | |
points: { | |
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 | |
}, | |
lines: { | |
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 | |
fillColor: null, // => fill color | |
fillOpacity: 0.4 // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill | |
}, | |
radar: { | |
show: false, // => setting to true will show radar chart, 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 | |
fillColor: null, // => fill color | |
fillOpacity: 0.4 // => opacity of the fill color, set to 1 for a solid fill, 0 hides the fill | |
}, | |
bars: { | |
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, | |
stacked: false | |
}, | |
candles: { | |
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 | |
barcharts: false // => draw as barcharts (not standard bars but financial barcharts) | |
}, | |
pie: { | |
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, | |
sizeRatio: 0.6, | |
startAngle: Math.PI/4, | |
labelFormatter: Flotr.defaultPieLabelFormatter, | |
pie3D: false, | |
pie3DviewAngle: (Math.PI/2 * 0.8), | |
pie3DspliceThickness: 20 | |
}, | |
grid: { | |
color: '#545454', // => primary color used for outline and labels | |
backgroundColor: null, // => null for transparent, else color | |
tickColor: '#DDDDDD', // => color used for the ticks | |
labelMargin: 3, // => margin in pixels | |
verticalLines: true, // => whether to show gridlines in vertical direction | |
horizontalLines: true, // => whether to show gridlines in horizontal direction | |
outlineWidth: 2 // => width of the grid outline/border in pixels | |
}, | |
selection: { | |
mode: null, // => one of null, 'x', 'y' or 'xy' | |
color: '#B6D9FF', // => selection box color | |
fps: 20 // => frames-per-second | |
}, | |
mouse: { | |
track: false, // => true to track the mouse, no tracking otherwise | |
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 | |
radius: 3 // => radius of the track point | |
}, | |
radarChartMode: false, // => true to render radar grid / and setup scaling for radar chart | |
shadowSize: 4, // => size of the 'fake' shadow | |
defaultType: 'lines', // => default series type | |
HtmlText: true, // => wether to draw the text using HTML or on the canvas | |
fontSize: 7.5, // => canvas' text font size | |
spreadsheet: { | |
show: false, // => show the data grid using two tabs | |
tabGraphLabel: 'Graph', | |
tabDataLabel: 'Data', | |
toolbarDownload: 'Download CSV', // @todo: add language support | |
toolbarSelectAll: 'Select all' | |
} | |
} | |
options.x2axis = Object.extend(Object.clone(options.xaxis), options.x2axis); | |
options.y2axis = Object.extend(Object.clone(options.yaxis), options.y2axis); | |
this.options = Flotr.merge((opts || {}), options); | |
this.axes = { | |
x: {options: this.options.xaxis, n: 1}, | |
x2: {options: this.options.x2axis, n: 2}, | |
y: {options: this.options.yaxis, n: 1}, | |
y2: {options: this.options.y2axis, n: 2} | |
}; | |
// 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, tooClose; | |
// Collect user-defined colors from series. | |
for(i = neededColors - 1; i > -1; --i){ | |
c = this.series[i].color; | |
if(c != null){ | |
--neededColors; | |
if(Object.isNumber(c)) assignedColors.push(c); | |
else usedColors.push(Flotr.parseColor(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.parseColor(oc[i]); | |
// Make sure each serie gets a different color. | |
var sign = variation % 2 == 1 ? -1 : 1; | |
var 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 == null){ | |
s.color = colors[j++].toString(); | |
}else if(Object.isNumber(s.color)){ | |
s.color = colors[s.color].toString(); | |
} | |
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. | |
s.lines = Object.extend(Object.clone(this.options.lines), s.lines); | |
s.points = Object.extend(Object.clone(this.options.points), s.points); | |
s.bars = Object.extend(Object.clone(this.options.bars), s.bars); | |
s.candles = Object.extend(Object.clone(this.options.candles), s.candles); | |
s.pie = Object.extend(Object.clone(this.options.pie), s.pie); | |
s.radar = Object.extend(Object.clone(this.options.radar), s.radar); | |
s.mouse = Object.extend(Object.clone(this.options.mouse), s.mouse); | |
if(s.shadowSize == null) s.shadowSize = this.options.shadowSize; | |
} | |
}, | |
/** | |
* 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. | |
*/ | |
constructCanvas: function(){ | |
var el = this.el, | |
size, c, oc; | |
this.canvas = el.select('.flotr-canvas')[0]; | |
this.overlay = el.select('.flotr-overlay')[0]; | |
el.childElements().invoke('remove'); | |
// For positioning labels and overlay. | |
el.setStyle({position:'relative', cursor:'default'}); | |
this.canvasWidth = el.getWidth(); | |
this.canvasHeight = el.getHeight(); | |
size = {'width': this.canvasWidth, 'height': this.canvasHeight}; | |
if(this.canvasWidth <= 0 || this.canvasHeight <= 0){ | |
throw 'Invalid dimensions for plot, width = ' + this.canvasWidth + ', height = ' + this.canvasHeight; | |
} | |
// Insert main canvas. | |
if (!this.canvas) { | |
c = this.canvas = new Element('canvas', size); | |
c.className = 'flotr-canvas'; | |
c = c.writeAttribute('style', 'position:absolute;left:0px;top:0px;'); | |
} else { | |
c = this.canvas.writeAttribute(size); | |
} | |
el.insert(c); | |
if(Prototype.Browser.IE){ | |
c = window.G_vmlCanvasManager.initElement(c); | |
} | |
this.ctx = c.getContext('2d'); | |
// Insert overlay canvas for interactive features. | |
if (!this.overlay) { | |
oc = this.overlay = new Element('canvas', size); | |
oc.className = 'flotr-overlay'; | |
oc = oc.writeAttribute('style', 'position:absolute;left:0px;top:0px;'); | |
} else { | |
oc = this.overlay.writeAttribute(size); | |
} | |
el.insert(oc); | |
if(Prototype.Browser.IE){ | |
oc = window.G_vmlCanvasManager.initElement(oc); | |
} | |
this.octx = oc.getContext('2d'); | |
// Enable text functions | |
if (window.CanvasText) { | |
CanvasText.enable(this.ctx); | |
CanvasText.enable(this.octx); | |
this.textEnabled = true; | |
} | |
}, | |
getTextDimensions: function(text, canvasStyle, HtmlStyle, className) { | |
if (!text) return {width:0, height:0}; | |
if (!this.options.HtmlText && this.textEnabled) { | |
var bounds = this.ctx.getTextBounds(text, canvasStyle); | |
return { | |
width: bounds.width+2, | |
height: bounds.height+6 | |
}; | |
} | |
else { | |
var dummyDiv = this.el.insert('<div style="position:absolute;top:-10000px;'+HtmlStyle+'" class="'+className+' flotr-dummy-div">' + text + '</div>').select(".flotr-dummy-div")[0]; | |
dim = dummyDiv.getDimensions(); | |
dummyDiv.remove(); | |
return dim; | |
} | |
}, | |
loadDataGrid: function(){ | |
if (this.seriesData) return this.seriesData; | |
var s = this.series; | |
var dg = []; | |
/* 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) | |
**/ | |
for(i = 0; i < s.length; ++i){ | |
s[i].data.each(function(v) { | |
var x = v[0], | |
y = v[1]; | |
if (r = dg.find(function(row) {return row[0] == x})) { | |
r[i+1] = y; | |
} | |
else { | |
var newRow = []; | |
newRow[0] = x; | |
newRow[i+1] = y | |
dg.push(newRow); | |
} | |
}); | |
} | |
// The data grid is sorted by x value | |
dg = dg.sortBy(function(v) { | |
return v[0]; | |
}); | |
return this.seriesData = dg; | |
}, | |
// @todo: make a tab manager (Flotr.Tabs) | |
showTab: function(tabName, onComplete){ | |
var elementsClassNames = 'canvas, .flotr-labels, .flotr-legend, .flotr-legend-bg, .flotr-title, .flotr-subtitle'; | |
switch(tabName) { | |
case 'graph': | |
this.datagrid.up().hide(); | |
this.el.select(elementsClassNames).invoke('show'); | |
this.tabs.data.removeClassName('selected'); | |
this.tabs.graph.addClassName('selected'); | |
break; | |
case 'data': | |
this.constructDataGrid(); | |
this.datagrid.up().show(); | |
this.el.select(elementsClassNames).invoke('hide'); | |
this.tabs.data.addClassName('selected'); | |
this.tabs.graph.removeClassName('selected'); | |
break; | |
} | |
}, | |
constructTabs: function(){ | |
var tabsContainer = new Element('div', {className:'flotr-tabs-group', style:'position:absolute;left:0px;top:'+this.canvasHeight+'px;width:'+this.canvasWidth+'px;'}); | |
this.el.insert({bottom: tabsContainer}); | |
this.tabs = { | |
graph: new Element('div', {className:'flotr-tab selected', style:'float:left;'}).update(this.options.spreadsheet.tabGraphLabel), | |
data: new Element('div', {className:'flotr-tab', style:'float:left;'}).update(this.options.spreadsheet.tabDataLabel) | |
} | |
tabsContainer.insert(this.tabs.graph).insert(this.tabs.data); | |
this.el.setStyle({height: this.canvasHeight+this.tabs.data.getHeight()+2+'px'}); | |
this.tabs.graph.observe('click', (function() {this.showTab('graph')}).bind(this)); | |
this.tabs.data.observe('click', (function() {this.showTab('data')}).bind(this)); | |
}, | |
// @todo: make a spreadsheet manager (Flotr.Spreadsheet) | |
constructDataGrid: function(){ | |
// If the data grid has already been built, nothing to do here | |
if (this.datagrid) return this.datagrid; | |
var i, j, | |
s = this.series, | |
datagrid = this.loadDataGrid(); | |
var t = this.datagrid = new Element('table', {className:'flotr-datagrid', style:'height:100px;'}); | |
var colgroup = ['<colgroup><col />']; | |
// First row : series' labels | |
var html = ['<tr class="first-row">']; | |
html.push('<th> </th>'); | |
for (i = 0; i < s.length; ++i) { | |
html.push('<th scope="col">'+(s[i].label || String.fromCharCode(65+i))+'</th>'); | |
colgroup.push('<col />'); | |
} | |
html.push('</tr>'); | |
// Data rows | |
for (j = 0; j < datagrid.length; ++j) { | |
html.push('<tr>'); | |
for (i = 0; i < s.length+1; ++i) { | |
var tag = 'td'; | |
var content = (datagrid[j][i] != null ? Math.round(datagrid[j][i]*100000)/100000 : ''); | |
if (i == 0) { | |
tag = 'th'; | |
var label; | |
if(this.options.xaxis.ticks) { | |
var tick = this.options.xaxis.ticks.find(function (x) { return x[0] == datagrid[j][i] }); | |
if (tick) label = tick[1]; | |
} | |
else { | |
label = this.options.xaxis.tickFormatter(content); | |
} | |
if (label) content = label; | |
} | |
html.push('<'+tag+(tag=='th'?' scope="row"':'')+'>'+content+'</'+tag+'>'); | |
} | |
html.push('</tr>'); | |
} | |
colgroup.push('</colgroup>'); | |
t.update(colgroup.join('')+html.join('')); | |
if (!Prototype.Browser.IE) { | |
t.select('td').each(function(td) { | |
td.observe('mouseover', function(e){ | |
td = e.element(); | |
var siblings = td.previousSiblings(); | |
t.select('th[scope=col]')[siblings.length-1].addClassName('hover'); | |
t.select('colgroup col')[siblings.length].addClassName('hover'); | |
}); | |
td.observe('mouseout', function(){ | |
t.select('colgroup col.hover, th.hover').each(function(e){e.removeClassName('hover')}); | |
}); | |
}); | |
} | |
var toolbar = new Element('div', {className: 'flotr-datagrid-toolbar'}). | |
insert(new Element('button', {type:'button', className:'flotr-datagrid-toolbar-button'}).update(this.options.spreadsheet.toolbarDownload).observe('click', this.downloadCSV.bind(this))). | |
insert(new Element('button', {type:'button', className:'flotr-datagrid-toolbar-button'}).update(this.options.spreadsheet.toolbarSelectAll).observe('click', this.selectAllData.bind(this))); | |
var container = new Element('div', {className:'flotr-datagrid-container', style:'left:0px;top:0px;width:'+this.canvasWidth+'px;height:'+this.canvasHeight+'px;overflow:auto;'}); | |
container.insert(toolbar); | |
t.wrap(container.hide()); | |
this.el.insert(container); | |
return t; | |
}, | |
selectAllData: function(){ | |
if (this.tabs) { | |
var selection, range, doc, win, node = this.constructDataGrid(); | |
this.showTab('data'); | |
// deferred to be able to select the table | |
(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(); | |
} | |
}).defer(); | |
return true; | |
} | |
else return false; | |
}, | |
downloadCSV: function(){ | |
var i, csv = '"x"', | |
series = this.series, | |
dg = this.loadDataGrid(); | |
for (i = 0; i < series.length; ++i) { | |
csv += '%09"'+(series[i].label || String.fromCharCode(65+i))+'"'; // \t | |
} | |
csv += "%0D%0A"; // \r\n | |
for (i = 0; i < dg.length; ++i) { | |
if (this.options.xaxis.ticks) { | |
var tick = this.options.xaxis.ticks.find(function (x) { return x[0] == dg[i][0] }); | |
if (tick) dg[i][0] = tick[1]; | |
} else { | |
dg[i][0] = this.options.xaxis.tickFormatter(dg[i][0]); | |
} | |
csv += dg[i].join('%09')+"%0D%0A"; // \t and \r\n | |
} | |
if (Prototype.Browser.IE) { | |
csv = csv.gsub('%09', '\t').gsub('%0A', '\n').gsub('%0D', '\r'); | |
window.open().document.write(csv); | |
} | |
else { | |
window.open('data:text/csv,'+csv); | |
} | |
}, | |
/** | |
* Initializes event some handlers. | |
*/ | |
initEvents: function () { | |
//@todo: maybe stopObserving with only flotr functions | |
this.overlay.stopObserving(); | |
this.overlay.observe('mousedown', this.mouseDownHandler.bind(this)); | |
this.overlay.observe('mousemove', this.mouseMoveHandler.bind(this)); | |
this.overlay.observe('click', this.clickHandler.bind(this)); | |
}, | |
/** | |
* Function determines the min and max values for the xaxis and yaxis. | |
*/ | |
findDataRanges: function(){ | |
var s = this.series, | |
a = this.axes; | |
a.x.datamin = 0; a.x.datamax = 0; | |
a.x2.datamin = 0; a.x2.datamax = 0; | |
a.y.datamin = 0; a.y.datamax = 0; | |
a.y2.datamin = 0; a.y2.datamax = 0; | |
if(s.length > 0){ | |
var i, j, h, x, y, data, xaxis, yaxis; | |
// Get datamin, datamax start values | |
for(i = 0; i < s.length; ++i) { | |
data = s[i].data, | |
xaxis = s[i].xaxis, | |
yaxis = s[i].yaxis; | |
if (data.length > 0 && !s[i].hide) { | |
if (!xaxis.used) xaxis.datamin = xaxis.datamax = data[0][0]; | |
if (!yaxis.used) yaxis.datamin = yaxis.datamax = data[0][1]; | |
xaxis.used = true; | |
yaxis.used = true; | |
for(h = data.length - 1; h > -1; --h){ | |
x = data[h][0]; | |
if(x < xaxis.datamin) xaxis.datamin = x; | |
else if(x > xaxis.datamax) xaxis.datamax = x; | |
for(j = 1; j < data[h].length; j++){ | |
y = data[h][j]; | |
if(y < yaxis.datamin) yaxis.datamin = y; | |
else if(y > yaxis.datamax) yaxis.datamax = y; | |
} | |
} | |
} | |
if (this.options.radarChartMode) { | |
xaxis.datamin = yaxis.datamin = - yaxis.datamax; | |
xaxis.datamax = yaxis.datamax; | |
if (!this.options.radarChartSides) this.options.radarChartSides = data.length; | |
} | |
} | |
} | |
this.findXAxesValues(); | |
this.calculateRange(a.x); | |
this.extendXRangeIfNeededByBar(a.x); | |
if (a.x2.used) { | |
this.calculateRange(a.x2); | |
this.extendXRangeIfNeededByBar(a.x2); | |
} | |
this.calculateRange(a.y); | |
this.extendYRangeIfNeededByBar(a.y); | |
if (a.y2.used) { | |
this.calculateRange(a.y2); | |
this.extendYRangeIfNeededByBar(a.y2); | |
} | |
}, | |
/** | |
* Calculates the range of an axis to apply autoscaling. | |
*/ | |
calculateRange: function(axis){ | |
var o = axis.options, | |
min = o.min != null ? o.min : axis.datamin, | |
max = o.max != null ? o.max : axis.datamax, | |
margin; | |
if(max - min == 0.0){ | |
var widen = (max == 0.0) ? 1.0 : 0.01; | |
min -= widen; | |
max += widen; | |
} | |
axis.tickSize = Flotr.getTickSize(o.noTicks, ((this.options.radarChartMode) ? 0 : min), max, o.tickDecimals); | |
// Autoscaling. | |
if(o.min == null){ | |
// Add a margin. | |
margin = o.autoscaleMargin; | |
if(margin != 0){ | |
min -= axis.tickSize * margin; | |
// Make sure we don't go below zero if all values are positive. | |
if(min < 0 && axis.datamin >= 0) min = 0; | |
min = axis.tickSize * Math.floor(min / axis.tickSize); | |
} | |
} | |
if(o.max == null){ | |
margin = o.autoscaleMargin; | |
if(margin != 0){ | |
max += axis.tickSize * margin; | |
if(max > 0 && axis.datamax <= 0) max = 0; | |
max = axis.tickSize * Math.ceil(max / axis.tickSize); | |
} | |
} | |
axis.min = min; | |
axis.max = max; | |
}, | |
/** | |
* Bar series autoscaling in x direction. | |
*/ | |
extendXRangeIfNeededByBar: function(axis){ | |
if(axis.options.max == null){ | |
var newmax = axis.max, | |
i, s, b, c, | |
stackedSums = [], | |
lastSerie = null; | |
for(i = 0; i < this.series.length; ++i){ | |
s = this.series[i]; | |
b = s.bars; | |
c = s.candles; | |
if(s.axis == axis && (b.show || c.show)) { | |
if (!b.horizontal && (b.barWidth + axis.datamax > newmax) || (c.candleWidth + axis.datamax > newmax)){ | |
newmax = axis.max + s.bars.barWidth; | |
} | |
if(b.stacked && b.horizontal){ | |
for (j = 0; j < s.data.length; j++) { | |
if (s.bars.show && s.bars.stacked) { | |
var x = s.data[j][0]; | |
stackedSums[x] = (stackedSums[x] || 0) + s.data[j][1]; | |
lastSerie = s; | |
} | |
} | |
for (j = 0; j < stackedSums.length; j++) { | |
newmax = Math.max(stackedSums[j], newmax); | |
} | |
} | |
} | |
} | |
axis.lastSerie = lastSerie; | |
axis.max = newmax; | |
} | |
}, | |
/** | |
* Bar series autoscaling in y direction. | |
*/ | |
extendYRangeIfNeededByBar: function(axis){ | |
if(axis.options.max == null){ | |
var newmax = axis.max, | |
i, s, b, c, | |
stackedSums = [], | |
lastSerie = null; | |
for(i = 0; i < this.series.length; ++i){ | |
s = this.series[i]; | |
b = s.bars; | |
c = s.candles; | |
if (s.yaxis == axis && b.show && !s.hide) { | |
if (b.horizontal && (b.barWidth + axis.datamax > newmax) || (c.candleWidth + axis.datamax > newmax)){ | |
newmax = axis.max + b.barWidth; | |
} | |
if(b.stacked && !b.horizontal){ | |
for (j = 0; j < s.data.length; j++) { | |
if (s.bars.show && s.bars.stacked) { | |
var x = s.data[j][0]; | |
stackedSums[x] = (stackedSums[x] || 0) + s.data[j][1]; | |
lastSerie = s; | |
} | |
} | |
for (j = 0; j < stackedSums.length; j++) { | |
newmax = Math.max(stackedSums[j], newmax); | |
} | |
} | |
} | |
} | |
axis.lastSerie = lastSerie; | |
axis.max = newmax; | |
} | |
}, | |
/** | |
* Find every values of the x axes | |
*/ | |
findXAxesValues: function(){ | |
for(i = this.series.length-1; i > -1 ; --i){ | |
s = this.series[i]; | |
s.xaxis.values = s.xaxis.values || []; | |
for (j = s.data.length-1; j > -1 ; --j){ | |
s.xaxis.values[s.data[j][0]] = {}; | |
} | |
} | |
}, | |
/** | |
* Calculate axis ticks. | |
* @param {Object} axis - axis object | |
* @param {Object} o - axis options | |
*/ | |
calculateTicks: function(axis){ | |
var o = axis.options, i, v; | |
axis.ticks = []; | |
if(o.ticks){ | |
var ticks = o.ticks, t, label; | |
if(Object.isFunction(ticks)){ | |
ticks = ticks({min: axis.min, max: axis.max}); | |
} | |
// Clean up the user-supplied ticks, copy them over. | |
for(i = 0; i < ticks.length; ++i){ | |
t = ticks[i]; | |
if(typeof(t) == 'object'){ | |
v = t[0]; | |
label = (t.length > 1) ? t[1] : o.tickFormatter(v); | |
}else{ | |
v = t; | |
label = o.tickFormatter(v); | |
} | |
axis.ticks[i] = { v: v, label: label }; | |
} | |
} | |
else { | |
// Round to nearest multiple of tick size. | |
var start = axis.tickSize * Math.ceil(axis.min / axis.tickSize), | |
decimals; | |
// Then store all possible ticks. | |
for(i = 0; start + i * axis.tickSize <= axis.max; ++i){ | |
v = start + i * axis.tickSize; | |
// Round (this is always needed to fix numerical instability). | |
decimals = o.tickDecimals; | |
if(decimals == null) decimals = 1 - Math.floor(Math.log(axis.tickSize) / Math.LN10); | |
if(decimals < 0) decimals = 0; | |
v = v.toFixed(decimals); | |
axis.ticks.push({ v: v, label: o.tickFormatter(v) }); | |
} | |
} | |
}, | |
/** | |
* Calculates axis label sizes. | |
*/ | |
calculateSpacing: function(){ | |
var a = this.axes, | |
options = this.options, | |
series = this.series, | |
margin = options.grid.labelMargin, | |
x = a.x, | |
x2 = a.x2, | |
y = a.y, | |
y2 = a.y2, | |
maxOutset = 2, | |
i, j, l, dim; | |
// Labels width and height | |
[x, x2, y, y2].each(function(axis) { | |
var maxLabel = ''; | |
if (axis.options.showLabels) { | |
for(i = 0; i < axis.ticks.length; ++i){ | |
l = axis.ticks[i].label.length; | |
if(l > maxLabel.length){ | |
maxLabel = axis.ticks[i].label; | |
} | |
} | |
} | |
axis.maxLabel = this.getTextDimensions(maxLabel, {size:options.fontSize, angle: Flotr.toRad(axis.options.labelsAngle)}, 'font-size:smaller;', 'flotr-grid-label'); | |
axis.titleSize = this.getTextDimensions(axis.options.title, {size: options.fontSize*1.2, angle: Flotr.toRad(axis.options.titleAngle)}, 'font-weight:bold;', 'flotr-axis-title'); | |
}, this); | |
// Title height | |
dim = this.getTextDimensions(options.title, {size: options.fontSize*1.5}, 'font-size:1em;font-weight:bold;', 'flotr-title'); | |
this.titleHeight = dim.height; | |
// Subtitle height | |
dim = this.getTextDimensions(options.subtitle, {size: options.fontSize}, 'font-size:smaller;', 'flotr-subtitle'); | |
this.subtitleHeight = dim.height; | |
// Grid outline line width. | |
if(options.show){ | |
maxOutset = Math.max(maxOutset, options.points.radius + options.points.lineWidth/2); | |
} | |
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 = {left: 0, right: 0, top: 0, bottom: 0}; | |
p.left = p.right = p.top = p.bottom = maxOutset; | |
p.bottom += (x.options.showLabels ? (x.maxLabel.height + margin) : 0) + | |
(x.options.title ? (x.titleSize.height + margin) : 0); | |
p.top += (x2.options.showLabels ? (x2.maxLabel.height + margin) : 0) + | |
(x2.options.title ? (x2.titleSize.height + margin) : 0) + this.subtitleHeight + this.titleHeight + | |
this.options.radarChartMode ? (y.options.showLabels ? (y.maxLabel.height + margin) : 0) : 0; | |
p.left += (y.options.showLabels ? (y.maxLabel.width + margin) : 0) + | |
(y.options.title ? (y.titleSize.width + margin) : 0); | |
p.right += (y2.options.showLabels ? (y2.maxLabel.width + margin) : 0) + | |
(y2.options.title ? (y2.titleSize.width + margin) : 0) + | |
this.options.radarChartMode ? (x.options.showLabels ? (x.maxLabel.width + margin) : 0) : 0; | |
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; | |
x.scale = this.plotWidth / (x.max - x.min); | |
x2.scale = this.plotWidth / (x2.max - x2.min); | |
y.scale = this.plotHeight / (y.max - y.min); | |
y2.scale = this.plotHeight / (y2.max - y2.min); | |
}, | |
/** | |
* Draws grid, labels and series. | |
*/ | |
draw: function() { | |
this.drawGrid(); | |
this.drawLabels(); | |
this.drawTitles(); | |
if(this.series.length){ | |
this.el.fire('flotr:beforedraw', [this.series, this]); | |
for(var i = 0; i < this.series.length; i++){ | |
if (!this.series[i].hide) | |
this.drawSeries(this.series[i]); | |
} | |
} | |
this.el.fire('flotr:afterdraw', [this.series, this]); | |
}, | |
/** | |
* Translates absolute horizontal x coordinates to relative coordinates. | |
* @param {Integer} x - absolute integer x coordinate | |
* @return {Integer} translated relative x coordinate | |
*/ | |
tHoz: function(x, axis){ | |
axis = axis || this.axes.x; | |
return (x - axis.min) * axis.scale; | |
}, | |
/** | |
* Translates absolute vertical x coordinates to relative coordinates. | |
* @param {Integer} y - absolute integer y coordinate | |
* @return {Integer} translated relative y coordinate | |
*/ | |
tVert: function(y, axis){ | |
axis = axis || this.axes.y; | |
return this.plotHeight - (y - axis.min) * axis.scale; | |
}, | |
/** | |
* Draws a grid for the graph. | |
*/ | |
drawGrid: function(){ | |
if (this.options.radarChartMode) { // If we are in radar chart mode call drawRadarGrid instead and exit | |
this.drawRadarGrid(); | |
return; | |
} | |
var v, o = this.options, | |
ctx = this.ctx; | |
if(o.grid.verticalLines || o.grid.horizontalLines){ | |
this.el.fire('flotr:beforegrid', [this.axes.x, this.axes.y, o, this]); | |
} | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
// Draw grid background, if present in options. | |
if(o.grid.backgroundColor != null){ | |
ctx.fillStyle = o.grid.backgroundColor; | |
ctx.fillRect(0, 0, this.plotWidth, this.plotHeight); | |
} | |
// Draw grid lines in vertical direction. | |
ctx.lineWidth = 1; | |
ctx.strokeStyle = o.grid.tickColor; | |
ctx.beginPath(); | |
if(o.grid.verticalLines){ | |
for(var i = 0; i < this.axes.x.ticks.length; ++i){ | |
v = this.axes.x.ticks[i].v; | |
// Don't show lines on upper and lower bounds. | |
if ((v == this.axes.x.min || v == this.axes.x.max) && o.grid.outlineWidth != 0) | |
continue; | |
ctx.moveTo(Math.floor(this.tHoz(v)) + ctx.lineWidth/2, 0); | |
ctx.lineTo(Math.floor(this.tHoz(v)) + ctx.lineWidth/2, this.plotHeight); | |
} | |
} | |
// Draw grid lines in horizontal direction. | |
if(o.grid.horizontalLines){ | |
for(var j = 0; j < this.axes.y.ticks.length; ++j){ | |
v = this.axes.y.ticks[j].v; | |
// Don't show lines on upper and lower bounds. | |
if ((v == this.axes.y.min || v == this.axes.y.max) && o.grid.outlineWidth != 0) | |
continue; | |
ctx.moveTo(0, Math.floor(this.tVert(v)) + ctx.lineWidth/2); | |
ctx.lineTo(this.plotWidth, Math.floor(this.tVert(v)) + ctx.lineWidth/2); | |
} | |
} | |
ctx.stroke(); | |
// Draw axis/grid border. | |
if(o.grid.outlineWidth != 0) { | |
ctx.lineWidth = o.grid.outlineWidth; | |
ctx.strokeStyle = o.grid.color; | |
ctx.lineJoin = 'round'; | |
ctx.strokeRect(0, 0, this.plotWidth, this.plotHeight); | |
} | |
ctx.restore(); | |
if(o.grid.verticalLines || o.grid.horizontalLines){ | |
this.el.fire('flotr:aftergrid', [this.axes.x, this.axes.y, o, this]); | |
} | |
}, | |
/** | |
* Draws a grid for the graph. | |
*/ | |
drawRadarGrid: function(){ | |
var v, o = this.options, | |
ctx = this.ctx; | |
var sides = this.options.radarChartSides, | |
degreesInRadiansForAngle = Math.PI * 2 / sides, | |
nintyDegrees = Math.PI / 2; | |
if(o.grid.verticalLines || o.grid.horizontalLines){ | |
this.el.fire('flotr:beforegrid', [this.axes.x, this.axes.y, o, this]); | |
} | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
ctx.lineJoin = 'round'; | |
// Draw grid background, if present in options. | |
if(o.grid.backgroundColor != null){ | |
ctx.fillStyle = o.grid.backgroundColor; | |
ctx.fillRect(0, 0, this.plotWidth, this.plotHeight); | |
} | |
// Draw grid lines | |
var regPoly = {}; | |
regPoly.xaxis = {}; | |
regPoly.yaxis = {}; | |
regPoly.xaxis.min = regPoly.yaxis.min = this.axes.x.min; | |
regPoly.xaxis.max = regPoly.yaxis.max = this.axes.x.max; | |
regPoly.xaxis.scale = this.plotWidth / (this.axes.x.max - this.axes.x.min); | |
regPoly.yaxis.scale = this.plotHeight / (this.axes.x.max - this.axes.x.min); | |
ctx.lineWidth = 1; | |
ctx.strokeStyle = o.grid.tickColor; | |
if(o.grid.horizontalLines){ | |
for(var j = 0; j < this.axes.y.ticks.length; ++j){ | |
v = this.axes.y.ticks[j].v; | |
if (v < 0) continue; | |
// Don't show lines on upper and lower bounds. | |
if ((v == this.axes.y.min || v == this.axes.y.max) && o.grid.outlineWidth != 0) | |
continue; | |
regPoly.data = new Array(); | |
for (i = 0; i < sides; i++) { | |
angle = nintyDegrees + (degreesInRadiansForAngle * i); | |
regPoly.data[i] = [v * Math.cos(angle), v * Math.sin(angle)] | |
} | |
regPoly.data[sides] = regPoly.data[0]; | |
this.plotLine(regPoly,0); | |
} | |
} | |
// Draw axis/grid border. | |
if(o.grid.outlineWidth != 0) { | |
ctx.lineWidth = o.grid.outlineWidth; | |
ctx.strokeStyle = o.grid.color; | |
regPoly.data = new Array(); | |
var radius = this.axes.x.max; | |
for (i = 0; i < sides; i++) { | |
angle = nintyDegrees + (degreesInRadiansForAngle * i); | |
regPoly.data[i] = [radius * Math.cos(angle), radius * Math.sin(angle)] | |
} | |
regPoly.data[sides] = regPoly.data[0]; | |
this.plotLine(regPoly,0); | |
} | |
ctx.lineWidth = 1; | |
ctx.strokeStyle = o.grid.tickColor; | |
ctx.beginPath(); | |
if(o.grid.verticalLines){ | |
for(var i = 0; i < sides; ++i){ | |
ctx.moveTo(Math.floor(this.tHoz(0)) + ctx.lineWidth/2, | |
Math.floor(this.tVert(0)) + ctx.lineWidth/2); | |
ctx.lineTo(Math.floor(this.tHoz(regPoly.data[i][0])) + ctx.lineWidth/2, | |
Math.floor(this.tVert(regPoly.data[i][1])) + ctx.lineWidth/2); | |
} | |
} | |
ctx.stroke(); | |
ctx.restore(); | |
if(o.grid.verticalLines || o.grid.horizontalLines){ | |
this.el.fire('flotr:aftergrid', [this.axes.x, this.axes.y, o, this]); | |
} | |
}, | |
/** | |
* Draws labels aroung radar chart | |
*/ | |
drawRadarLabels:function(){ | |
var ctx = this.ctx, | |
options = this.options, | |
axis = this.axes.x, | |
tick, minY = 0, maxY = 0, | |
xOffset, yOffset; | |
var style = { | |
size: options.fontSize, | |
adjustAlign: true | |
}; | |
style.color = axis.options.color || options.grid.color; | |
style.angle = Flotr.toRad(axis.options.labelsAngle); | |
var radius = axis.max * 1, | |
closeTo = axis.max * 0.1, | |
sides = this.options.radarChartSides, | |
degreesInRadiansForAngle = Math.PI * 2 / sides, | |
nintyDegrees = Math.PI / 2, | |
posdata = new Array(); | |
for (i = 0; i < sides; i++) { | |
angle = nintyDegrees + (degreesInRadiansForAngle * i); | |
posdata[i] = [radius * Math.cos(angle), radius * Math.sin(angle)]; | |
if (minY > posdata[i][1]) minY = posdata[i][1]; | |
if (maxY < posdata[i][1]) maxY = posdata[i][1]; | |
} | |
for (i = 0; i < sides; i++) { | |
tick = axis.ticks[i]; | |
if(!tick.label || tick.label.length == 0) continue; | |
yOffset = 0; | |
if (posdata[i][0] > 0) { | |
style.halign = 'l'; | |
xOffset = options.grid.labelMargin; | |
} else { | |
style.halign = 'r'; | |
xOffset = - options.grid.labelMargin; | |
} | |
style.valign = 'm'; | |
if ((posdata[i][1] + closeTo) >= minY && (posdata[i][1] - closeTo) <= minY) { | |
style.valign = 't' ; | |
style.halign = 'c'; | |
yOffset = options.grid.labelMargin; | |
}; | |
if (posdata[i][1] == maxY) { | |
style.valign = 'b' ; | |
style.halign = 'c'; | |
yOffset = - options.grid.labelMargin; | |
} | |
ctx.drawText( | |
tick.label, | |
this.plotOffset.left + this.tHoz(posdata[i][0]) + xOffset, | |
this.plotOffset.top + this.tVert(posdata[i][1]) + yOffset, | |
style | |
); | |
} | |
}, | |
/** | |
* Draws labels for x and y axis. | |
*/ | |
drawLabels: function(){ | |
// Construct fixed width label boxes, which can be styled easily. | |
var noLabels = 0, axis, | |
xBoxWidth, i, html, tick, | |
options = this.options, | |
ctx = this.ctx, | |
a = this.axes; | |
for(i = 0; i < a.x.ticks.length; ++i){ | |
if (a.x.ticks[i].label) { | |
++noLabels; | |
} | |
} | |
xBoxWidth = this.plotWidth / noLabels; | |
if (!options.HtmlText && this.textEnabled) { | |
var style = { | |
size: options.fontSize, | |
adjustAlign: true | |
}; | |
// Add x labels. | |
if (options.radarChartMode) { | |
this.drawRadarLabels();} else { | |
axis = a.x; | |
style.color = axis.options.color || options.grid.color; | |
for(i = 0; i < axis.ticks.length && axis.options.showLabels && axis.used; ++i){ | |
tick = axis.ticks[i]; | |
if(!tick.label || tick.label.length == 0) continue; | |
style.angle = Flotr.toRad(axis.options.labelsAngle); | |
style.halign = 'c'; | |
style.valign = 't'; | |
ctx.drawText( | |
tick.label, | |
this.plotOffset.left + this.tHoz(tick.v, axis), | |
this.plotOffset.top + this.plotHeight + options.grid.labelMargin, | |
style | |
); | |
}} | |
// Add x2 labels. | |
axis = a.x2; | |
style.color = axis.options.color || options.grid.color; | |
for(i = 0; i < axis.ticks.length && axis.options.showLabels && axis.used; ++i){ | |
tick = axis.ticks[i]; | |
if(!tick.label || tick.label.length == 0) continue; | |
style.angle = Flotr.toRad(axis.options.labelsAngle); | |
style.halign = 'c'; | |
style.valign = 'b'; | |
ctx.drawText( | |
tick.label, | |
this.plotOffset.left + this.tHoz(tick.v, axis), | |
this.plotOffset.top + options.grid.labelMargin, | |
style | |
); | |
} | |
// Add y labels. | |
axis = a.y; | |
style.color = axis.options.color || options.grid.color; | |
for(i = 0; i < axis.ticks.length && axis.options.showLabels && axis.used; ++i){ | |
tick = axis.ticks[i]; | |
if (!tick.label || tick.label.length == 0 || (tick.v < 0 && this.options.radarChartMode)) continue; | |
style.angle = Flotr.toRad(axis.options.labelsAngle); | |
style.halign = 'r'; | |
style.valign = 'm'; | |
ctx.drawText( | |
tick.label, | |
this.plotOffset.left + (this.options.radarChartMode ? this.tHoz(0) : 0) - options.grid.labelMargin, | |
this.plotOffset.top + this.tVert(tick.v, axis), | |
style | |
); | |
} | |
// Add y2 labels. | |
axis = a.y2; | |
style.color = axis.options.color || options.grid.color; | |
for(i = 0; i < axis.ticks.length && axis.options.showLabels && axis.used; ++i){ | |
tick = axis.ticks[i]; | |
if (!tick.label || tick.label.length == 0) continue; | |
style.angle = Flotr.toRad(axis.options.labelsAngle); | |
style.halign = 'l'; | |
style.valign = 'm'; | |
ctx.drawText( | |
tick.label, | |
this.plotOffset.left + this.plotWidth + options.grid.labelMargin, | |
this.plotOffset.top + this.tVert(tick.v, axis), | |
style | |
); | |
ctx.save(); | |
ctx.strokeStyle = style.color; | |
ctx.beginPath(); | |
ctx.moveTo(this.plotOffset.left + this.plotWidth - 8, this.plotOffset.top + this.tVert(tick.v, axis)); | |
ctx.lineTo(this.plotOffset.left + this.plotWidth, this.plotOffset.top + this.tVert(tick.v, axis)); | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
} | |
else if (a.x.options.showLabels || | |
a.x2.options.showLabels || | |
a.y.options.showLabels || | |
a.y2.options.showLabels) { | |
html = ['<div style="font-size:smaller;color:' + options.grid.color + ';" class="flotr-labels">']; | |
// Add x labels. | |
axis = a.x; | |
if (axis.options.showLabels){ | |
for(i = 0; i < axis.ticks.length; ++i){ | |
tick = axis.ticks[i]; | |
if(!tick.label || tick.label.length == 0) continue; | |
html.push('<div style="position:absolute;top:' + (this.plotOffset.top + this.plotHeight + options.grid.labelMargin) + 'px;left:' + (this.plotOffset.left + this.tHoz(tick.v, axis) - xBoxWidth/2) + 'px;width:' + xBoxWidth + 'px;text-align:center;'+(axis.options.color?('color:'+axis.options.color+';'):'')+'" class="flotr-grid-label">' + tick.label + '</div>'); | |
} | |
} | |
// Add x2 labels. | |
axis = a.x2; | |
if (axis.options.showLabels && axis.used){ | |
for(i = 0; i < axis.ticks.length; ++i){ | |
tick = axis.ticks[i]; | |
if(!tick.label || tick.label.length == 0) continue; | |
html.push('<div style="position:absolute;top:' + (this.plotOffset.top - options.grid.labelMargin - axis.maxLabel.height) + 'px;left:' + (this.plotOffset.left + this.tHoz(tick.v, axis) - xBoxWidth/2) + 'px;width:' + xBoxWidth + 'px;text-align:center;'+(axis.options.color?('color:'+axis.options.color+';'):'')+'" class="flotr-grid-label">' + tick.label + '</div>'); | |
} | |
} | |
// Add y labels. | |
axis = a.y; | |
if (axis.options.showLabels){ | |
for(i = 0; i < axis.ticks.length; ++i){ | |
tick = axis.ticks[i]; | |
if (!tick.label || tick.label.length == 0) continue; | |
html.push('<div style="position:absolute;top:' + (this.plotOffset.top + this.tVert(tick.v, axis) - axis.maxLabel.height/2) + 'px;left:0;width:' + (this.plotOffset.left - options.grid.labelMargin) + 'px;text-align:right;'+(axis.options.color?('color:'+axis.options.color+';'):'')+'" class="flotr-grid-label">' + tick.label + '</div>'); | |
} | |
} | |
// Add y2 labels. | |
axis = a.y2; | |
if (axis.options.showLabels && axis.used){ | |
ctx.save(); | |
ctx.strokeStyle = axis.options.color || options.grid.color; | |
ctx.beginPath(); | |
for(i = 0; i < axis.ticks.length; ++i){ | |
tick = axis.ticks[i]; | |
if (!tick.label || tick.label.length == 0) continue; | |
html.push('<div style="position:absolute;top:' + (this.plotOffset.top + this.tVert(tick.v, axis) - axis.maxLabel.height/2) + 'px;right:0;width:' + (this.plotOffset.right - options.grid.labelMargin) + 'px;text-align:left;'+(axis.options.color?('color:'+axis.options.color+';'):'')+'" class="flotr-grid-label">' + tick.label + '</div>'); | |
ctx.moveTo(this.plotOffset.left + this.plotWidth - 8, this.plotOffset.top + this.tVert(tick.v, axis)); | |
ctx.lineTo(this.plotOffset.left + this.plotWidth, this.plotOffset.top + this.tVert(tick.v, axis)); | |
} | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
html.push('</div>'); | |
this.el.insert(html.join('')); | |
} | |
}, | |
/** | |
* 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, | |
halign: 'c' | |
}; | |
// Add subtitle | |
if (options.subtitle){ | |
ctx.drawText( | |
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){ | |
ctx.drawText( | |
options.title, | |
this.plotOffset.left + this.plotWidth/2, | |
this.titleHeight - 2, | |
style | |
); | |
} | |
style.weight = 1.8; | |
style.size *= 0.8; | |
style.adjustAlign = true; | |
// Add x axis title | |
if (a.x.options.title && a.x.used){ | |
style.halign = 'c'; | |
style.valign = 't'; | |
style.angle = Flotr.toRad(a.x.options.titleAngle); | |
ctx.drawText( | |
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.halign = 'c'; | |
style.valign = 'b'; | |
style.angle = Flotr.toRad(a.x2.options.titleAngle); | |
ctx.drawText( | |
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.halign = 'r'; | |
style.valign = 'm'; | |
style.angle = Flotr.toRad(a.y.options.titleAngle); | |
ctx.drawText( | |
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.halign = 'l'; | |
style.valign = 'm'; | |
style.angle = Flotr.toRad(a.y2.options.titleAngle); | |
ctx.drawText( | |
a.y2.options.title, | |
this.plotOffset.left + this.plotWidth + a.y2.maxLabel.width + 2 * margin, | |
this.plotOffset.top + this.plotHeight / 2, | |
style | |
); | |
} | |
} | |
else { | |
html = ['<div style="color:'+options.grid.color+';" class="flotr-titles">']; | |
// 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.push('</div>'); | |
this.el.insert(html.join('')); | |
} | |
}, | |
/** | |
* Actually draws the graph. | |
* @param {Object} series - series to draw | |
*/ | |
drawSeries: function(series){ | |
series = series || this.series; | |
var drawn = false; | |
for(var type in Flotr._registeredTypes){ | |
if(series[type] && series[type].show){ | |
this[Flotr._registeredTypes[type]](series); | |
drawn = true; | |
} | |
} | |
if(!drawn){ | |
this[Flotr._registeredTypes[this.options.defaultType]](series); | |
} | |
}, | |
plotLine: function(series, offset){ | |
var ctx = this.ctx, | |
xa = series.xaxis, | |
ya = series.yaxis, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this), | |
data = series.data; | |
if(data.length < 2) return; | |
var prevx = tHoz(data[0][0], xa), | |
prevy = tVert(data[0][1], ya) + offset; | |
ctx.beginPath(); | |
ctx.moveTo(prevx, prevy); | |
for(var i = 0; i < data.length - 1; ++i){ | |
var x1 = data[i][0], y1 = data[i][1], | |
x2 = data[i+1][0], y2 = data[i+1][1]; | |
// To allow empty values | |
if (y1 === null || y2 === null) continue; | |
/** | |
* Clip with ymin. | |
*/ | |
if(y1 <= y2 && y1 < ya.min){ | |
/** | |
* Line segment is outside the drawing area. | |
*/ | |
if(y2 < ya.min) continue; | |
/** | |
* Compute new intersection point. | |
*/ | |
x1 = (ya.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = ya.min; | |
}else if(y2 <= y1 && y2 < ya.min){ | |
if(y1 < ya.min) continue; | |
x2 = (ya.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = ya.min; | |
} | |
/** | |
* Clip with ymax. | |
*/ | |
if(y1 >= y2 && y1 > ya.max) { | |
if(y2 > ya.max) continue; | |
x1 = (ya.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = ya.max; | |
} | |
else if(y2 >= y1 && y2 > ya.max){ | |
if(y1 > ya.max) continue; | |
x2 = (ya.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = ya.max; | |
} | |
/** | |
* Clip with xmin. | |
*/ | |
if(x1 <= x2 && x1 < xa.min){ | |
if(x2 < xa.min) continue; | |
y1 = (xa.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = xa.min; | |
}else if(x2 <= x1 && x2 < xa.min){ | |
if(x1 < xa.min) continue; | |
y2 = (xa.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = xa.min; | |
} | |
/** | |
* Clip with xmax. | |
*/ | |
if(x1 >= x2 && x1 > xa.max){ | |
if (x2 > xa.max) continue; | |
y1 = (xa.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = xa.max; | |
}else if(x2 >= x1 && x2 > xa.max){ | |
if(x1 > xa.max) continue; | |
y2 = (xa.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = xa.max; | |
} | |
if(prevx != tHoz(x1, xa) || prevy != tVert(y1, ya) + offset) | |
ctx.moveTo(tHoz(x1, xa), tVert(y1, ya) + offset); | |
prevx = tHoz(x2, xa); | |
prevy = tVert(y2, ya) + offset; | |
ctx.lineTo(prevx, prevy); | |
} | |
ctx.stroke(); | |
}, | |
/** | |
* Function used to fill | |
* @param {Object} data | |
*/ | |
plotLineArea: function(series, offset){ | |
var data = series.data; | |
if(data.length < 2) return; | |
var top, lastX = 0, | |
ctx = this.ctx, | |
xa = series.xaxis, | |
ya = series.yaxis, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this), | |
bottom = Math.min(Math.max(0, ya.min), ya.max), | |
first = true; | |
ctx.beginPath(); | |
for(var i = 0; i < data.length - 1; ++i){ | |
var x1 = data[i][0], y1 = data[i][1], | |
x2 = data[i+1][0], y2 = data[i+1][1]; | |
if(x1 <= x2 && x1 < xa.min){ | |
if(x2 < xa.min) continue; | |
y1 = (xa.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = xa.min; | |
}else if(x2 <= x1 && x2 < xa.min){ | |
if(x1 < xa.min) continue; | |
y2 = (xa.min - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = xa.min; | |
} | |
if(x1 >= x2 && x1 > xa.max){ | |
if(x2 > xa.max) continue; | |
y1 = (xa.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x1 = xa.max; | |
}else if(x2 >= x1 && x2 > xa.max){ | |
if (x1 > xa.max) continue; | |
y2 = (xa.max - x1) / (x2 - x1) * (y2 - y1) + y1; | |
x2 = xa.max; | |
} | |
if(first){ | |
ctx.moveTo(tHoz(x1, xa), tVert(bottom, ya) + offset); | |
first = false; | |
} | |
/** | |
* Now check the case where both is outside. | |
*/ | |
if(y1 >= ya.max && y2 >= ya.max){ | |
ctx.lineTo(tHoz(x1, xa), tVert(ya.max, ya) + offset); | |
ctx.lineTo(tHoz(x2, xa), tVert(ya.max, ya) + offset); | |
continue; | |
}else if(y1 <= ya.min && y2 <= ya.min){ | |
ctx.lineTo(tHoz(x1, xa), tVert(ya.min, ya) + offset); | |
ctx.lineTo(tHoz(x2, xa), tVert(ya.min, ya) + offset); | |
continue; | |
} | |
/** | |
* Else it's a bit more complicated, there might | |
* be two rectangles and two triangles we need to fill | |
* in; to find these keep track of the current x values. | |
*/ | |
var x1old = x1, x2old = x2; | |
/** | |
* And clip the y values, without shortcutting. | |
* Clip with ymin. | |
*/ | |
if(y1 <= y2 && y1 < ya.min && y2 >= ya.min){ | |
x1 = (ya.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = ya.min; | |
}else if(y2 <= y1 && y2 < ya.min && y1 >= ya.min){ | |
x2 = (ya.min - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = ya.min; | |
} | |
/** | |
* Clip with ymax. | |
*/ | |
if(y1 >= y2 && y1 > ya.max && y2 <= ya.max){ | |
x1 = (ya.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y1 = ya.max; | |
}else if(y2 >= y1 && y2 > ya.max && y1 <= ya.max){ | |
x2 = (ya.max - y1) / (y2 - y1) * (x2 - x1) + x1; | |
y2 = ya.max; | |
} | |
/** | |
* If the x value was changed we got a rectangle to fill. | |
*/ | |
if(x1 != x1old){ | |
top = (y1 <= ya.min) ? top = ya.min : ya.max; | |
ctx.lineTo(tHoz(x1old, xa), tVert(top, ya) + offset); | |
ctx.lineTo(tHoz(x1, xa), tVert(top, ya) + offset); | |
} | |
/** | |
* Fill the triangles. | |
*/ | |
ctx.lineTo(tHoz(x1, xa), tVert(y1, ya) + offset); | |
ctx.lineTo(tHoz(x2, xa), tVert(y2, ya) + offset); | |
/** | |
* Fill the other rectangle if it's there. | |
*/ | |
if(x2 != x2old){ | |
top = (y2 <= ya.min) ? ya.min : ya.max; | |
ctx.lineTo(tHoz(x2old, xa), tVert(top, ya) + offset); | |
ctx.lineTo(tHoz(x2, xa), tVert(top, ya) + offset); | |
} | |
lastX = Math.max(x2, x2old); | |
} | |
ctx.lineTo(tHoz(lastX, xa), tVert(bottom, ya) + offset); | |
ctx.closePath(); | |
ctx.fill(); | |
}, | |
/** | |
* Function: (private) drawSeriesLines | |
* | |
* Function draws lines series in the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.lines.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesLines: function(series){ | |
series = series || this.series; | |
var ctx = this.ctx; | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
ctx.lineJoin = 'round'; | |
var lw = series.lines.lineWidth; | |
var sw = series.shadowSize; | |
if(sw > 0){ | |
ctx.lineWidth = sw / 2; | |
var offset = lw/2 + ctx.lineWidth/2; | |
ctx.strokeStyle = "rgba(0,0,0,0.1)"; | |
this.plotLine(series, offset + sw/2); | |
ctx.strokeStyle = "rgba(0,0,0,0.2)"; | |
this.plotLine(series, offset); | |
if(series.lines.fill) { | |
ctx.fillStyle = "rgba(0,0,0,0.05)"; | |
this.plotLineArea(series, offset + sw/2); | |
} | |
} | |
ctx.lineWidth = lw; | |
ctx.strokeStyle = series.color; | |
if(series.lines.fill){ | |
ctx.fillStyle = series.lines.fillColor != null ? series.lines.fillColor : Flotr.parseColor(series.color).scale(null, null, null, series.lines.fillOpacity).toString(); | |
this.plotLineArea(series, 0); | |
} | |
this.plotLine(series, 0); | |
ctx.restore(); | |
}, | |
/** | |
* Function: drawSeriesPoints | |
* | |
* Function draws point series in the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.points.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesPoints: function(series) { | |
var ctx = this.ctx; | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
var lw = series.lines.lineWidth; | |
var sw = series.shadowSize; | |
if(sw > 0){ | |
ctx.lineWidth = sw / 2; | |
ctx.strokeStyle = 'rgba(0,0,0,0.1)'; | |
this.plotPointShadows(series, sw/2 + ctx.lineWidth/2, series.points.radius); | |
ctx.strokeStyle = 'rgba(0,0,0,0.2)'; | |
this.plotPointShadows(series, ctx.lineWidth/2, series.points.radius); | |
} | |
ctx.lineWidth = series.points.lineWidth; | |
ctx.strokeStyle = series.color; | |
ctx.fillStyle = series.points.fillColor != null ? series.points.fillColor : series.color; | |
this.plotPoints(series, series.points.radius, series.points.fill); | |
ctx.restore(); | |
}, | |
plotPoints: function (series, radius, fill) { | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
ctx = this.ctx, i, | |
data = series.data; | |
for(i = data.length - 1; i > -1; --i){ | |
var x = data[i][0], y = data[i][1]; | |
if(x < xa.min || x > xa.max || y < ya.min || y > ya.max) | |
continue; | |
ctx.beginPath(); | |
ctx.arc(this.tHoz(x, xa), this.tVert(y, ya), radius, 0, 2 * Math.PI, true); | |
if(fill) ctx.fill(); | |
ctx.stroke(); | |
} | |
}, | |
plotPointShadows: function(series, offset, radius){ | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
ctx = this.ctx, i, | |
data = series.data; | |
for(i = data.length - 1; i > -1; --i){ | |
var x = data[i][0], y = data[i][1]; | |
if (x < xa.min || x > xa.max || y < ya.min || y > ya.max) | |
continue; | |
ctx.beginPath(); | |
ctx.arc(this.tHoz(x, xa), this.tVert(y, ya) + offset, radius, 0, Math.PI, false); | |
ctx.stroke(); | |
} | |
}, | |
/** | |
* Function: drawSeriesBars | |
* | |
* Function draws bar series in the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.bars.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesBars: function(series) { | |
var ctx = this.ctx, | |
bw = series.bars.barWidth, | |
lw = Math.min(series.bars.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; | |
this.plotBarsShadows(series, bw, 0, series.bars.fill); | |
if(series.bars.fill){ | |
ctx.fillStyle = series.bars.fillColor != null ? series.bars.fillColor : Flotr.parseColor(series.color).scale(null, null, null, series.bars.fillOpacity).toString(); | |
} | |
this.plotBars(series, bw, 0, series.bars.fill); | |
ctx.restore(); | |
}, | |
plotBars: function(series, barWidth, offset, fill){ | |
var data = series.data; | |
if(data.length < 1) return; | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
ctx = this.ctx, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this); | |
for(var i = 0; i < data.length; i++){ | |
var x = data[i][0], | |
y = data[i][1]; | |
var drawLeft = true, drawTop = true, drawRight = true; | |
// Stacked bars | |
var stackOffset = 0; | |
if(series.bars.stacked) { | |
xa.values.each(function(o, v) { | |
if (v == x) { | |
stackOffset = o.stack || 0; | |
o.stack = stackOffset + y; | |
} | |
}); | |
} | |
// @todo: fix horizontal bars support | |
// Horizontal bars | |
if(series.bars.horizontal) | |
var left = stackOffset, right = x + stackOffset, bottom = y, top = y + barWidth; | |
else | |
var left = x, right = x + barWidth, bottom = stackOffset, top = y + stackOffset; | |
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 && series.bars.horizontal) | |
drawTop = false; | |
} | |
if(bottom < ya.min) | |
bottom = ya.min; | |
if(top > ya.max){ | |
top = ya.max; | |
if (ya.lastSerie != series && !series.bars.horizontal) | |
drawTop = false; | |
} | |
/** | |
* Fill the bar. | |
*/ | |
if(fill){ | |
ctx.beginPath(); | |
ctx.moveTo(tHoz(left, xa), tVert(bottom, ya) + offset); | |
ctx.lineTo(tHoz(left, xa), tVert(top, ya) + offset); | |
ctx.lineTo(tHoz(right, xa), tVert(top, ya) + offset); | |
ctx.lineTo(tHoz(right, xa), tVert(bottom, ya) + offset); | |
ctx.fill(); | |
} | |
/** | |
* Draw bar outline/border. | |
*/ | |
if(series.bars.lineWidth != 0 && (drawLeft || drawRight || drawTop)){ | |
ctx.beginPath(); | |
ctx.moveTo(tHoz(left, xa), tVert(bottom, ya) + offset); | |
ctx[drawLeft ?'lineTo':'moveTo'](tHoz(left, xa), tVert(top, ya) + offset); | |
ctx[drawTop ?'lineTo':'moveTo'](tHoz(right, xa), tVert(top, ya) + offset); | |
ctx[drawRight?'lineTo':'moveTo'](tHoz(right, xa), tVert(bottom, ya) + offset); | |
ctx.stroke(); | |
} | |
} | |
}, | |
plotBarsShadows: function(series, barWidth, offset){ | |
var data = series.data; | |
if(data.length < 1) return; | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
ctx = this.ctx, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this), | |
sw = this.options.shadowSize; | |
for(var i = 0; i < data.length; i++){ | |
var x = data[i][0], | |
y = data[i][1]; | |
// Stacked bars | |
var stackOffset = 0; | |
if(series.bars.stacked) { | |
xa.values.each(function(o, v) { | |
if (v == x) { | |
stackOffset = o.stackShadow || 0; | |
o.stackShadow = stackOffset + y; | |
} | |
}); | |
} | |
// Horizontal bars | |
if(series.bars.horizontal) | |
var left = stackOffset, right = x + stackOffset, bottom = y, top = y + barWidth; | |
else | |
var left = x, right = x + barWidth, bottom = stackOffset, top = y + stackOffset; | |
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 = tHoz(right, xa)-tHoz(left, xa)-((tHoz(right, xa)+sw <= this.plotWidth) ? 0 : sw); | |
var height = Math.max(0, tVert(bottom, ya)-tVert(top, ya)-((tVert(bottom, ya)+sw <= this.plotHeight) ? 0 : sw)); | |
ctx.fillStyle = 'rgba(0,0,0,0.05)'; | |
ctx.fillRect(Math.min(tHoz(left, xa)+sw, this.plotWidth), Math.min(tVert(top, ya)+sw, this.plotWidth), width, height); | |
} | |
}, | |
/** | |
* Function: drawSeriesCandles | |
* | |
* Function draws candles series in the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.candles.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesCandles: function(series) { | |
var ctx = this.ctx, | |
bw = series.candles.candleWidth; | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
ctx.lineJoin = 'miter'; | |
/** | |
* @todo linewidth not interpreted the right way. | |
*/ | |
ctx.lineWidth = series.candles.lineWidth; | |
this.plotCandlesShadows(series, bw/2); | |
this.plotCandles(series, bw/2); | |
ctx.restore(); | |
}, | |
plotCandles: function(series, offset){ | |
var data = series.data; | |
if(data.length < 1) return; | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
ctx = this.ctx, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this); | |
for(var i = 0; i < data.length; i++){ | |
var d = data[i], | |
x = d[0], | |
open = d[1], | |
high = d[2], | |
low = d[3], | |
close = d[4]; | |
var left = x, | |
right = x + series.candles.candleWidth, | |
bottom = Math.max(ya.min, low), | |
top = Math.min(ya.max, high), | |
bottom2 = Math.max(ya.min, Math.min(open, close)), | |
top2 = Math.min(ya.max, Math.max(open, close)); | |
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max) | |
continue; | |
var color = series.candles[open>close?'downFillColor':'upFillColor']; | |
/** | |
* Fill the candle. | |
*/ | |
if(series.candles.fill && !series.candles.barcharts){ | |
ctx.fillStyle = Flotr.parseColor(color).scale(null, null, null, series.candles.fillOpacity).toString(); | |
ctx.fillRect(tHoz(left, xa), tVert(top2, ya) + offset, tHoz(right, xa) - tHoz(left, xa), tVert(bottom2, ya) - tVert(top2, ya)); | |
} | |
/** | |
* Draw candle outline/border, high, low. | |
*/ | |
if(series.candles.lineWidth || series.candles.wickLineWidth){ | |
var x, y, pixelOffset = (series.candles.wickLineWidth % 2) / 2; | |
x = Math.floor(tHoz((left + right) / 2), xa) + pixelOffset; | |
ctx.save(); | |
ctx.strokeStyle = color; | |
ctx.lineWidth = series.candles.wickLineWidth; | |
ctx.lineCap = 'butt'; | |
if (series.candles.barcharts) { | |
ctx.beginPath(); | |
ctx.moveTo(x, Math.floor(tVert(top, ya) + offset)); | |
ctx.lineTo(x, Math.floor(tVert(bottom, ya) + offset)); | |
y = Math.floor(tVert(open, ya) + offset)+0.5; | |
ctx.moveTo(Math.floor(tHoz(left, xa))+pixelOffset, y); | |
ctx.lineTo(x, y); | |
y = Math.floor(tVert(close, ya) + offset)+0.5; | |
ctx.moveTo(Math.floor(tHoz(right, xa))+pixelOffset, y); | |
ctx.lineTo(x, y); | |
} | |
else { | |
ctx.strokeRect(tHoz(left, xa), tVert(top2, ya) + offset, tHoz(right, xa) - tHoz(left, xa), tVert(bottom2, ya) - tVert(top2, ya)); | |
ctx.beginPath(); | |
ctx.moveTo(x, Math.floor(tVert(top2, ya) + offset)); | |
ctx.lineTo(x, Math.floor(tVert(top, ya) + offset)); | |
ctx.moveTo(x, Math.floor(tVert(bottom2, ya) + offset)); | |
ctx.lineTo(x, Math.floor(tVert(bottom, ya) + offset)); | |
} | |
ctx.stroke(); | |
ctx.restore(); | |
} | |
} | |
}, | |
plotCandlesShadows: function(series, offset){ | |
var data = series.data; | |
if(data.length < 1 || series.candles.barcharts) return; | |
var xa = series.xaxis, | |
ya = series.yaxis, | |
tHoz = this.tHoz.bind(this), | |
tVert = this.tVert.bind(this), | |
sw = this.options.shadowSize; | |
for(var i = 0; i < data.length; i++){ | |
var d = data[i], | |
x = d[0], | |
open = d[1], | |
high = d[2], | |
low = d[3], | |
close = d[4]; | |
var left = x, | |
right = x + series.candles.candleWidth, | |
bottom = Math.max(ya.min, Math.min(open, close)), | |
top = Math.min(ya.max, Math.max(open, close)); | |
if(right < xa.min || left > xa.max || top < ya.min || bottom > ya.max) | |
continue; | |
var width = tHoz(right, xa)-tHoz(left, xa)-((tHoz(right, xa)+sw <= this.plotWidth) ? 0 : sw); | |
var height = Math.max(0, tVert(bottom, ya)-tVert(top, ya)-((tVert(bottom, ya)+sw <= this.plotHeight) ? 0 : sw)); | |
this.ctx.fillStyle = 'rgba(0,0,0,0.05)'; | |
this.ctx.fillRect(Math.min(tHoz(left, xa)+sw, this.plotWidth), Math.min(tVert(top, ya)+sw, this.plotWidth), width, height); | |
} | |
}, | |
/** | |
* Function: drawSeriesRadar | |
* | |
* Function draws a radar chart on the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.radar.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesRadar: function(series) { | |
var ctx = this.ctx, | |
options = this.options, sides= series.data.length; | |
var degreesInRadiansForAngle = Math.PI * 2 / sides, | |
nintyDegrees = Math.PI / 2; | |
var poly = {}; | |
/* | |
Draw radar grid | |
poly.xaxis = series.xaxis; | |
poly.yaxis = series.yaxis; | |
ctx.save(); | |
ctx.translate(this.plotOffset.left, this.plotOffset.top); | |
ctx.lineJoin = 'round'; | |
for (radius = 20; radius <= 100; radius += 20) { | |
poly.data = new Array(); | |
for (i = 0; i < sides; i++) { | |
angle = nintyDegrees + (degreesInRadiansForAngle * i); | |
poly.data[i] = [radius * Math.cos(angle), radius * Math.sin(angle)] | |
} | |
poly.data[sides] = poly.data[0]; | |
this.plotLine(poly,0);} | |
var outside = poly.data; | |
for (i = 0; i < sides; i++) { | |
poly.data = new Array(); | |
poly.data[0] = [0,0]; | |
poly.data[1] = outside[i]; | |
this.plotLine(poly,0); | |
} | |
*/ | |
/* | |
Convert Series data into X, Y co-ordinates | |
*/ | |
if (!series.dataInRadarFormat) { | |
poly.data = new Array(); | |
for (i = 0; i < sides; i++) { | |
angle = nintyDegrees + (degreesInRadiansForAngle * i); | |
poly.data[i] = [series.data[i][1] * Math.cos(angle), series.data[i][1] * Math.sin(angle), series.data[i][0], series.data[i][1]] | |
} | |
poly.data[sides] = poly.data[0]; | |
series.data = poly.data; | |
series.lines = series.radar; | |
series.lines.show = false; | |
series.dataInRadarFormat = true; | |
} | |
this.drawSeriesLines(series); | |
}, | |
/** | |
* Function: drawSeriesPie | |
* | |
* Function draws a pie in the canvas element. | |
* | |
* Parameters: | |
* series - Series with options.pie.show = true. | |
* | |
* Returns: | |
* void | |
*/ | |
drawSeriesPie: function(series) { | |
if (!this.options.pie.drawn) { | |
var ctx = this.ctx, | |
options = this.options, | |
lw = series.pie.lineWidth, | |
sw = series.shadowSize, | |
data = series.data, | |
radius = (Math.min(this.canvasWidth, this.canvasHeight) * series.pie.sizeRatio) / 2, | |
html = []; | |
var vScale = 1;//Math.cos(series.pie.viewAngle); | |
var plotTickness = Math.sin(series.pie.viewAngle)*series.pie.spliceThickness / vScale; | |
var style = { | |
size: options.fontSize*1.2, | |
color: options.grid.color, | |
weight: 1.5 | |
}; | |
var center = { | |
x: (this.canvasWidth+this.plotOffset.left)/2, | |
y: (this.canvasHeight-this.plotOffset.bottom)/2 | |
}; | |
// Pie portions | |
var portions = this.series.collect(function(hash, index){ | |
if (hash.pie.show) | |
return { | |
name: (hash.label || hash.data[0][1]), | |
value: [index, hash.data[0][1]], | |
explode: hash.pie.explode | |
}; | |
}); | |
// Sum of the portions' angles | |
var sum = portions.pluck('value').pluck(1).inject(0, function(acc, n) { return acc + n; }); | |
var fraction = 0.0, | |
angle = series.pie.startAngle, | |
value = 0.0; | |
var slices = portions.collect(function(slice){ | |
angle += fraction; | |
value = parseFloat(slice.value[1]); // @warning : won't support null values !! | |
fraction = value/sum; | |
return { | |
name: slice.name, | |
fraction: fraction, | |
x: slice.value[0], | |
y: value, | |
explode: slice.explode, | |
startAngle: 2 * angle * Math.PI, | |
endAngle: 2 * (angle + fraction) * Math.PI | |
}; | |
}); | |
ctx.save(); | |
if(sw > 0){ | |
slices.each(function (slice) { | |
var bisection = (slice.startAngle + slice.endAngle) / 2; | |
var xOffset = center.x + Math.cos(bisection) * slice.explode + sw; | |
var yOffset = center.y + Math.sin(bisection) * slice.explode + sw; | |
this.plotSlice(xOffset, yOffset, radius, slice.startAngle, slice.endAngle, false, vScale); | |
ctx.fillStyle = 'rgba(0,0,0,0.1)'; | |
ctx.fill(); | |
}, this); | |
} | |
if (options.HtmlText) { | |
html = ['<div style="color:' + this.options.grid.color + '" class="flotr-labels">']; | |
} | |
slices.each(function (slice, index) { | |
var bisection = (slice.startAngle + slice.endAngle) / 2; | |
var color = options.colors[index]; | |
var xOffset = center.x + Math.cos(bisection) * slice.explode; | |
var yOffset = center.y + Math.sin(bisection) * slice.explode; | |
this.plotSlice(xOffset, yOffset, radius, slice.startAngle, slice.endAngle, false, vScale); | |
if(series.pie.fill){ | |
ctx.fillStyle = Flotr.parseColor(color).scale(null, null, null, series.pie.fillOpacity).toString(); | |
ctx.fill(); | |
} | |
ctx.lineWidth = lw; | |
ctx.strokeStyle = color; | |
ctx.stroke(); | |
/*ctx.save(); | |
ctx.scale(1, vScale); | |
ctx.moveTo(xOffset, yOffset); | |
ctx.beginPath(); | |
ctx.lineTo(xOffset, yOffset+plotTickness); | |
ctx.lineTo(xOffset+Math.cos(slice.startAngle)*radius, yOffset+Math.sin(slice.startAngle)*radius+plotTickness); | |
ctx.lineTo(xOffset+Math.cos(slice.startAngle)*radius, yOffset+Math.sin(slice.startAngle)*radius); | |
ctx.lineTo(xOffset, yOffset); | |
ctx.closePath(); | |
ctx.fill();ctx.stroke(); | |
ctx.moveTo(xOffset, yOffset); | |
ctx.beginPath(); | |
ctx.lineTo(xOffset, yOffset+plotTickness); | |
ctx.lineTo(xOffset+Math.cos(slice.endAngle)*radius, yOffset+Math.sin(slice.endAngle)*radius+plotTickness); | |
ctx.lineTo(xOffset+Math.cos(slice.endAngle)*radius, yOffset+Math.sin(slice.endAngle)*radius); | |
ctx.lineTo(xOffset, yOffset); | |
ctx.closePath(); | |
ctx.fill();ctx.stroke(); | |
ctx.moveTo(xOffset+Math.cos(slice.startAngle)*radius, yOffset+Math.sin(slice.startAngle)*radius); | |
ctx.beginPath(); | |
ctx.lineTo(xOffset+Math.cos(slice.startAngle)*radius, yOffset+Math.sin(slice.startAngle)*radius+plotTickness); | |
ctx.arc(xOffset, yOffset+plotTickness, radius, slice.startAngle, slice.endAngle, false); | |
ctx.lineTo(xOffset+Math.cos(slice.endAngle)*radius, yOffset+Math.sin(slice.endAngle)*radius); | |
ctx.arc(xOffset, yOffset, radius, slice.endAngle, slice.startAngle, true); | |
ctx.closePath(); | |
ctx.fill();ctx.stroke(); | |
ctx.scale(1, 1/vScale); | |
this.plotSlice(xOffset, yOffset+plotTickness, radius, slice.startAngle, slice.endAngle, false, vScale); | |
ctx.stroke(); | |
if(series.pie.fill){ | |
ctx.fillStyle = Flotr.parseColor(color).scale(null, null, null, series.pie.fillOpacity).toString(); | |
ctx.fill(); | |
} | |
ctx.restore();*/ | |
var label = options.pie.labelFormatter(slice); | |
var textAlignRight = (Math.cos(bisection) < 0); | |
var distX = xOffset + Math.cos(bisection) * (series.pie.explode + radius); | |
var distY = yOffset + Math.sin(bisection) * (series.pie.explode + radius); | |
if (slice.fraction && label) { | |
if (options.HtmlText) { | |
var divStyle = 'position:absolute;top:' + (distY - 5) + 'px;'; //@todo: change | |
if (textAlignRight) { | |
divStyle += 'right:'+(this.canvasWidth - distX)+'px;text-align:right;'; | |
} | |
else { | |
divStyle += 'left:'+distX+'px;text-align:left;'; | |
} | |
html.push('<div style="' + divStyle + '" class="flotr-grid-label">' + label + '</div>'); | |
} | |
else { | |
style.halign = textAlignRight ? 'r' : 'l'; | |
ctx.drawText( | |
label, | |
distX, | |
distY + style.size / 2, | |
style | |
); | |
} | |
} | |
}, this); | |
if (options.HtmlText) { | |
html.push('</div>'); | |
this.el.insert(html.join('')); | |
} | |
ctx.restore(); | |
options.pie.drawn = true; | |
} | |
}, | |
plotSlice: function(x, y, radius, startAngle, endAngle, fill, vScale) { | |
var ctx = this.ctx; | |
vScale = vScale || 1; | |
ctx.save(); | |
ctx.scale(1, vScale); | |
ctx.beginPath(); | |
ctx.moveTo(x, y); | |
ctx.arc (x, y, radius, startAngle, endAngle, fill); | |
ctx.lineTo(x, y); | |
ctx.closePath(); | |
ctx.restore(); | |
}, | |
plotPie: function() {}, | |
/** | |
* Function: insertLegend | |
* | |
* Function adds a legend div to the canvas container or draws it on the canvas. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
insertLegend: function(){ | |
if(!this.options.legend.show) | |
return; | |
var series = this.series, | |
plotOffset = this.plotOffset, | |
options = this.options, | |
fragments = [], | |
rowStarted = false, | |
ctx = this.ctx, | |
i; | |
var noLegendItems = series.findAll(function(s) {return (s.label && !s.hide)}).size(); | |
if (noLegendItems) { | |
if (!options.HtmlText && this.textEnabled) { | |
var style = { | |
size: options.fontSize*1.1, | |
color: options.grid.color | |
}; | |
// @todo: take css into account | |
//var dummyDiv = this.el.insert('<div class="flotr-legend" style="position:absolute;top:-10000px;"></div>'); | |
var p = options.legend.position, | |
m = options.legend.margin, | |
lbw = options.legend.labelBoxWidth, | |
lbh = options.legend.labelBoxHeight, | |
lbm = options.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; | |
var label = options.legend.labelFormatter(series[i].label); | |
labelMaxWidth = Math.max(labelMaxWidth, ctx.measureText(label, style)); | |
} | |
var legendWidth = Math.round(lbw + lbm*3 + labelMaxWidth), | |
legendHeight = Math.round(noLegendItems*(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 | |
var color = Flotr.parseColor(options.legend.backgroundColor || 'rgb(240,240,240)').scale(null, null, null, options.legend.backgroundOpacity || 0.1).toString(); | |
ctx.fillStyle = color; | |
ctx.fillRect(offsetX, offsetY, legendWidth, legendHeight); | |
ctx.strokeStyle = options.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; | |
var label = options.legend.labelFormatter(series[i].label); | |
ctx.fillStyle = series[i].color; | |
ctx.fillRect(x, y, lbw-1, lbh-1); | |
ctx.strokeStyle = options.legend.labelBoxBorderColor; | |
ctx.lineWidth = 1; | |
ctx.strokeRect(Math.ceil(x)-1.5, Math.ceil(y)-1.5, lbw+2, lbh+2); | |
// Legend text | |
ctx.drawText( | |
label, | |
x + lbw + lbm, | |
y + (lbh + style.size - ctx.fontDescent(style))/2, | |
style | |
); | |
y += lbh + lbm; | |
} | |
} | |
else { | |
for(i = 0; i < series.length; ++i){ | |
if(!series[i].label || series[i].hide) continue; | |
if(i % options.legend.noColumns == 0){ | |
fragments.push(rowStarted ? '</tr><tr>' : '<tr>'); | |
rowStarted = true; | |
} | |
var label = options.legend.labelFormatter(series[i].label); | |
fragments.push('<td class="flotr-legend-color-box"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:' + options.legend.labelBoxWidth + 'px;height:' + options.legend.labelBoxHeight + 'px;background-color:' + series[i].color + '"></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(options.legend.container != null){ | |
$(options.legend.container).update(table); | |
}else{ | |
var pos = ''; | |
var p = options.legend.position, m = options.legend.margin; | |
if(p.charAt(0) == 'n') pos += 'top:' + (m + plotOffset.top) + 'px;'; | |
else if(p.charAt(0) == 's') pos += 'bottom:' + (m + plotOffset.bottom) + 'px;'; | |
if(p.charAt(1) == 'e') pos += 'right:' + (m + plotOffset.right) + 'px;'; | |
else if(p.charAt(1) == 'w') pos += 'left:' + (m + plotOffset.left) + 'px;'; | |
var div = this.el.insert('<div class="flotr-legend" style="position:absolute;z-index:2;' + pos +'">' + table + '</div>').select('div.flotr-legend').first(); | |
if(options.legend.backgroundOpacity != 0.0){ | |
/** | |
* Put in the transparent background separately to avoid blended labels and | |
* label boxes. | |
*/ | |
var c = options.legend.backgroundColor; | |
if(c == null){ | |
var tmp = (options.grid.backgroundColor != null) ? options.grid.backgroundColor : Flotr.extractColor(div); | |
c = Flotr.parseColor(tmp).adjust(null, null, null, 1).toString(); | |
} | |
this.el.insert('<div class="flotr-legend-bg" style="position:absolute;width:' + div.getWidth() + 'px;height:' + div.getHeight() + 'px;' + pos +'background-color:' + c + ';"> </div>').select('div.flotr-legend-bg').first().setStyle({ | |
'opacity': options.legend.backgroundOpacity | |
}); | |
} | |
} | |
} | |
} | |
} | |
}, | |
/** | |
* Function: getEventPosition | |
* | |
* Calculates the coordinates from a mouse event object. | |
* | |
* Parameters: | |
* event - Mouse Event object. | |
* | |
* Returns: | |
* Object with x and y coordinates of the mouse. | |
*/ | |
getEventPosition: function (event){ | |
var offset = this.overlay.cumulativeOffset(), | |
rx = (event.pageX - offset.left - this.plotOffset.left), | |
ry = (event.pageY - offset.top - this.plotOffset.top), | |
ax = 0, ay = 0 | |
if(event.pageX == null && event.clientX != null){ | |
var de = document.documentElement, b = document.body; | |
ax = event.clientX + (de && de.scrollLeft || b.scrollLeft || 0); | |
ay = event.clientY + (de && de.scrollTop || b.scrollTop || 0); | |
}else{ | |
ax = event.pageX; | |
ay = event.pageY; | |
} | |
return { | |
x: this.axes.x.min + rx / this.axes.x.scale, | |
x2: this.axes.x2.min + rx / this.axes.x2.scale, | |
y: this.axes.y.max - ry / this.axes.y.scale, | |
y2: this.axes.y2.max - ry / this.axes.y2.scale, | |
relX: rx, | |
relY: ry, | |
absX: ax, | |
absY: ay | |
}; | |
}, | |
/** | |
* Function: clickHandler | |
* | |
* Handler observes the 'click' event and fires the 'flotr:click' event. | |
* | |
* Parameters: | |
* event - 'click' Event object. | |
* | |
* Returns: | |
* void | |
*/ | |
clickHandler: function(event){ | |
if(this.ignoreClick){ | |
this.ignoreClick = false; | |
return; | |
} | |
this.el.fire('flotr:click', [this.getEventPosition(event), this]); | |
}, | |
/** | |
* Function: mouseMoveHandler | |
* | |
* Handler observes mouse movement over the graph area. Fires the | |
* 'flotr:mousemove' event. | |
* | |
* Parameters: | |
* event - 'mousemove' Event object. | |
* | |
* Returns: | |
* void | |
*/ | |
mouseMoveHandler: function(event){ | |
var pos = this.getEventPosition(event); | |
this.lastMousePos.pageX = pos.absX; | |
this.lastMousePos.pageY = pos.absY; | |
if(this.selectionInterval == null && (this.options.mouse.track || this.series.any(function(s){return s.mouse && s.mouse.track;}))){ | |
this.hit(pos); | |
} | |
this.el.fire('flotr:mousemove', [event, pos, this]); | |
}, | |
/** | |
* Function: mouseDownHandler | |
* | |
* Handler observes the 'mousedown' event. | |
* | |
* Parameters: | |
* event - 'mousedown' Event object. | |
* | |
* Returns: | |
* void | |
*/ | |
mouseDownHandler: function (event){ | |
if(event.isRightClick()) { | |
event.stop(); | |
var overlay = this.overlay; | |
overlay.hide(); | |
function cancelContextMenu () { | |
overlay.show(); | |
$(document).stopObserving('mousemove', cancelContextMenu); | |
} | |
$(document).observe('mousemove', cancelContextMenu); | |
return; | |
} | |
if(!this.options.selection.mode || !event.isLeftClick()) return; | |
this.setSelectionPos(this.selection.first, event); | |
if(this.selectionInterval != null){ | |
clearInterval(this.selectionInterval); | |
} | |
this.lastMousePos.pageX = null; | |
this.selectionInterval = setInterval(this.updateSelection.bind(this), 1000/this.options.selection.fps); | |
this.mouseUpHandler = this.mouseUpHandler.bind(this); | |
$(document).observe('mouseup', this.mouseUpHandler); | |
}, | |
/** | |
* Function: (private) fireSelectEvent | |
* | |
* Fires the 'flotr:select' event when the user made a selection. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
fireSelectEvent: function(){ | |
var a = this.axes, selection = this.selection, | |
x1 = (selection.first.x <= selection.second.x) ? selection.first.x : selection.second.x, | |
x2 = (selection.first.x <= selection.second.x) ? selection.second.x : selection.first.x, | |
y1 = (selection.first.y >= selection.second.y) ? selection.first.y : selection.second.y, | |
y2 = (selection.first.y >= selection.second.y) ? selection.second.y : selection.first.y; | |
x1 = a.x.min + x1 / a.x.scale; | |
x2 = a.x.min + x2 / a.x.scale; | |
y1 = a.y.max - y1 / a.y.scale; | |
y2 = a.y.max - y2 / a.y.scale; | |
this.el.fire('flotr:select', [{x1:x1, y1:y1, x2:x2, y2:y2}, this]); | |
}, | |
/** | |
* Function: (private) mouseUpHandler | |
* | |
* Handler observes the mouseup event for the document. | |
* | |
* Parameters: | |
* event - 'mouseup' Event object. | |
* | |
* Returns: | |
* void | |
*/ | |
mouseUpHandler: function(event){ | |
$(document).stopObserving('mouseup', this.mouseUpHandler); | |
event.stop(); | |
if(this.selectionInterval != null){ | |
clearInterval(this.selectionInterval); | |
this.selectionInterval = null; | |
} | |
this.setSelectionPos(this.selection.second, event); | |
this.clearSelection(); | |
if(this.selectionIsSane()){ | |
this.drawSelection(); | |
this.fireSelectEvent(); | |
this.ignoreClick = true; | |
} | |
}, | |
/** | |
* Function: setSelectionPos | |
* | |
* Calculates the position of the selection. | |
* | |
* Parameters: | |
* pos - Position object. | |
* event - Event object. | |
* | |
* Returns: | |
* void | |
*/ | |
setSelectionPos: function(pos, event) { | |
var options = this.options, | |
offset = $(this.overlay).cumulativeOffset(); | |
if(options.selection.mode.indexOf('x') == -1){ | |
pos.x = (pos == this.selection.first) ? 0 : this.plotWidth; | |
}else{ | |
pos.x = event.pageX - offset.left - this.plotOffset.left; | |
pos.x = Math.min(Math.max(0, pos.x), this.plotWidth); | |
} | |
if (options.selection.mode.indexOf('y') == -1){ | |
pos.y = (pos == this.selection.first) ? 0 : this.plotHeight; | |
}else{ | |
pos.y = event.pageY - offset.top - this.plotOffset.top; | |
pos.y = Math.min(Math.max(0, pos.y), this.plotHeight); | |
} | |
}, | |
/** | |
* Function: updateSelection | |
* | |
* Updates (draws) the selection box. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
updateSelection: function(){ | |
if(this.lastMousePos.pageX == null) return; | |
this.setSelectionPos(this.selection.second, this.lastMousePos); | |
this.clearSelection(); | |
if(this.selectionIsSane()) this.drawSelection(); | |
}, | |
/** | |
* Function: clearSelection | |
* | |
* Removes the selection box from the overlay canvas. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
clearSelection: function() { | |
if(this.prevSelection == null) return; | |
var prevSelection = this.prevSelection, | |
octx = this.octx, | |
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); | |
octx.clearRect(x + plotOffset.left - octx.lineWidth, | |
y + plotOffset.top - octx.lineWidth, | |
w + octx.lineWidth*2, | |
h + octx.lineWidth*2); | |
this.prevSelection = null; | |
}, | |
/** | |
* Function: setSelection | |
* | |
* Allows the user the manually select an area. | |
* | |
* Parameters: | |
* area - Object with coordinates to select. | |
* | |
* Returns: | |
* void | |
*/ | |
setSelection: function(area){ | |
var options = this.options, | |
xa = this.axes.x, | |
ya = this.axes.y, | |
vertScale = yaxis.scale, | |
hozScale = xaxis.scale, | |
selX = options.selection.mode.indexOf('x') != -1, | |
selY = options.selection.mode.indexOf('y') != -1; | |
this.clearSelection(); | |
this.selection.first.y = selX ? 0 : (ya.max - area.y1) * vertScale; | |
this.selection.second.y = selX ? this.plotHeight : (ya.max - area.y2) * vertScale; | |
this.selection.first.x = selY ? 0 : (area.x1 - xa.min) * hozScale; | |
this.selection.second.x = selY ? this.plotWidth : (area.x2 - xa.min) * hozScale; | |
this.drawSelection(); | |
this.fireSelectEvent(); | |
}, | |
/** | |
* Function: (private) drawSelection | |
* | |
* Draws the selection box. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
drawSelection: function() { | |
var prevSelection = this.prevSelection, | |
selection = this.selection, | |
octx = this.octx, | |
options = this.options, | |
plotOffset = this.plotOffset; | |
if(prevSelection != null && | |
selection.first.x == prevSelection.first.x && | |
selection.first.y == prevSelection.first.y && | |
selection.second.x == prevSelection.second.x && | |
selection.second.y == prevSelection.second.y) | |
return; | |
octx.strokeStyle = Flotr.parseColor(options.selection.color).scale(null, null, null, 0.8).toString(); | |
octx.lineWidth = 1; | |
octx.lineJoin = 'round'; | |
octx.fillStyle = Flotr.parseColor(options.selection.color).scale(null, null, null, 0.4).toString(); | |
this.prevSelection = { | |
first: { x: selection.first.x, y: selection.first.y }, | |
second: { x: selection.second.x, y: selection.second.y } | |
}; | |
var x = Math.min(selection.first.x, selection.second.x), | |
y = Math.min(selection.first.y, selection.second.y), | |
w = Math.abs(selection.second.x - selection.first.x), | |
h = Math.abs(selection.second.y - selection.first.y); | |
octx.fillRect(x + plotOffset.left, y + plotOffset.top, w, h); | |
octx.strokeRect(x + plotOffset.left, y + plotOffset.top, w, h); | |
}, | |
/** | |
* Function: (private) selectionIsSane | |
* | |
* Determines whether or not the selection is sane and should be drawn. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* boolean - True when sane, false otherwise. | |
*/ | |
selectionIsSane: function(){ | |
var selection = this.selection; | |
return Math.abs(selection.second.x - selection.first.x) >= 5 && | |
Math.abs(selection.second.y - selection.first.y) >= 5; | |
}, | |
/** | |
* Function: clearHit | |
* | |
* Removes the mouse tracking point from the overlay. | |
* | |
* Parameters: | |
* none | |
* | |
* Returns: | |
* void | |
*/ | |
clearHit: function(){ | |
if(this.prevHit){ | |
var options = this.options, | |
plotOffset = this.plotOffset, | |
prevHit = this.prevHit; | |
this.octx.clearRect( | |
this.tHoz(prevHit.x) + plotOffset.left - options.points.radius*2, | |
this.tVert(prevHit.y) + plotOffset.top - options.points.radius*2, | |
options.points.radius*3 + options.points.lineWidth*3, | |
options.points.radius*3 + options.points.lineWidth*3 | |
); | |
this.prevHit = null; | |
} | |
}, | |
/** | |
* Function: hit | |
* | |
* 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. | |
* | |
* Parameters: | |
* mouse - Object that holds the relative x and y coordinates of the cursor. | |
* | |
* Returns: | |
* void | |
*/ | |
hit: function(mouse){ | |
var series = this.series, | |
options = this.options, | |
prevHit = this.prevHit, | |
plotOffset = this.plotOffset, | |
octx = this.octx, | |
data, xsens, ysens, | |
/** | |
* Nearest data element. | |
*/ | |
i, n = { | |
dist:Number.MAX_VALUE, | |
x:null, | |
y:null, | |
relX:mouse.relX, | |
relY:mouse.relY, | |
absX:mouse.absX, | |
absY:mouse.absY, | |
mouse:null, | |
radarData:null | |
}; | |
for(i = 0; i < series.length; i++){ | |
s = series[i]; | |
if(!s.mouse.track) continue; | |
data = s.data; | |
xsens = (s.xaxis.scale*s.mouse.sensibility); | |
ysens = (s.yaxis.scale*s.mouse.sensibility); | |
for(var j = 0, xpow, ypow; j < data.length; j++){ | |
if (data[j][1] === null) continue; | |
xpow = Math.pow(s.xaxis.scale*(data[j][0] - mouse.x), 2); | |
ypow = Math.pow(s.yaxis.scale*(data[j][1] - mouse.y), 2); | |
if(xpow < xsens && ypow < ysens && Math.sqrt(xpow+ypow) < n.dist){ | |
n.dist = Math.sqrt(xpow+ypow); | |
n.x = data[j][0]; | |
n.y = data[j][1]; | |
n.radarLabel = data[j][2]; | |
n.radarData = data[j][3]; | |
n.mouse = s.mouse; | |
} | |
} | |
} | |
if(n.mouse && n.mouse.track && !prevHit || (prevHit && (n.x != prevHit.x || n.y != prevHit.y))){ | |
var mt = this.mouseTrack || this.el.select(".flotr-mouse-value")[0], | |
pos = '', | |
p = options.mouse.position, | |
m = options.mouse.margin, | |
elStyle = '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;'; | |
if (!options.mouse.relative) { // absolute to the canvas | |
if(p.charAt(0) == 'n') pos += 'top:' + (m + plotOffset.top) + 'px;'; | |
else if(p.charAt(0) == 's') pos += 'bottom:' + (m + plotOffset.bottom) + 'px;'; | |
if(p.charAt(1) == 'e') pos += 'right:' + (m + plotOffset.right) + 'px;'; | |
else if(p.charAt(1) == 'w') pos += 'left:' + (m + plotOffset.left) + 'px;'; | |
} | |
else { // relative to the mouse | |
if(p.charAt(0) == 'n') pos += 'bottom:' + (m - plotOffset.top - this.tVert(n.y) + this.canvasHeight) + 'px;'; | |
else if(p.charAt(0) == 's') pos += 'top:' + (m + plotOffset.top + this.tVert(n.y)) + 'px;'; | |
if(p.charAt(1) == 'e') pos += 'left:' + (m + plotOffset.left + this.tHoz(n.x)) + 'px;'; | |
else if(p.charAt(1) == 'w') pos += 'right:' + (m - plotOffset.left - this.tHoz(n.x) + this.canvasWidth) + 'px;'; | |
} | |
elStyle += pos; | |
if(!mt){ | |
this.el.insert('<div class="flotr-mouse-value" style="'+elStyle+'"></div>'); | |
mt = this.mouseTrack = this.el.select('.flotr-mouse-value').first(); | |
} | |
else { | |
this.mouseTrack = mt.setStyle(elStyle); | |
} | |
if(n.x !== null && n.y !== null){ | |
mt.show(); | |
this.clearHit(); | |
if(n.mouse.lineColor != null){ | |
octx.save(); | |
octx.translate(plotOffset.left, plotOffset.top); | |
octx.lineWidth = options.points.lineWidth; | |
octx.strokeStyle = n.mouse.lineColor; | |
octx.fillStyle = '#ffffff'; | |
octx.beginPath(); | |
octx.arc(this.tHoz(n.x), this.tVert(n.y), options.mouse.radius, 0, 2 * Math.PI, true); | |
octx.fill(); | |
octx.stroke(); | |
octx.restore(); | |
} | |
this.prevHit = n; | |
var decimals = n.mouse.trackDecimals; | |
if(decimals == null || decimals < 0) decimals = 0; | |
mt.innerHTML = n.mouse.trackFormatter({x: n.x.toFixed(decimals), y: n.y.toFixed(decimals), | |
radarLabel: n.radarLabel, radarData: n.radarData.toFixed(decimals)}); | |
mt.fire('flotr:hit', [n, this]); | |
} | |
else if(prevHit){ | |
mt.hide(); | |
this.clearHit(); | |
} | |
} | |
}, | |
saveImage: function (type, width, height, replaceCanvas) { | |
var image = null; | |
switch (type) { | |
case 'jpeg': | |
case 'jpg': image = Canvas2Image.saveAsJPEG(this.canvas, replaceCanvas, width, height); break; | |
default: | |
case 'png': image = Canvas2Image.saveAsPNG(this.canvas, replaceCanvas, width, height); break; | |
case 'bmp': image = Canvas2Image.saveAsBMP(this.canvas, replaceCanvas, width, height); break; | |
} | |
if (Object.isElement(image) && replaceCanvas) { | |
this.restoreCanvas(); | |
this.canvas.hide(); | |
this.overlay.hide(); | |
this.el.insert(image.setStyle({position: 'absolute'})); | |
} | |
}, | |
restoreCanvas: function() { | |
this.canvas.show(); | |
this.overlay.show(); | |
this.el.select('img').invoke('remove'); | |
} | |
}); | |
Flotr.Color = Class.create({ | |
initialize: function(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(); | |
}, | |
adjust: function(rd, gd, bd, ad) { | |
var x = 4; | |
while(-1<--x){ | |
if(arguments[x] != null) | |
this[this.rgba[x]] += arguments[x]; | |
} | |
return this.normalize(); | |
}, | |
clone: function(){ | |
return new Flotr.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), 0, 255); | |
this.g = limit(parseInt(this.g), 0, 255); | |
this.b = limit(parseInt(this.b), 0, 255); | |
this.a = limit(this.a, 0, 1); | |
return this; | |
}, | |
scale: function(rf, gf, bf, af){ | |
var x = 4; | |
while(-1<--x){ | |
if(arguments[x] != null) | |
this[this.rgba[x]] *= arguments[x]; | |
} | |
return this.normalize(); | |
}, | |
distance: function(color){ | |
if (!color) return; | |
color = new Flotr.parseColor(color); | |
var dist = 0; | |
var 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(',')+')'; | |
} | |
}); | |
Flotr.Color.lookupColors = { | |
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] | |
}; | |
// not used yet | |
Flotr.Date = { | |
format: function(d, format) { | |
if (!d) return; | |
var leftPad = function(n) { | |
n = n.toString(); | |
return n.length == 1 ? "0" + n : n; | |
}; | |
var r = []; | |
var escape = false; | |
for (var i = 0; i < format.length; ++i) { | |
var c = format.charAt(i); | |
if (escape) { | |
switch (c) { | |
case 'h': c = d.getUTCHours().toString(); break; | |
case 'H': c = leftPad(d.getUTCHours()); break; | |
case 'M': c = leftPad(d.getUTCMinutes()); break; | |
case 'S': c = leftPad(d.getUTCSeconds()); break; | |
case 'd': c = d.getUTCDate().toString(); break; | |
case 'm': c = (d.getUTCMonth() + 1).toString(); break; | |
case 'y': c = d.getUTCFullYear().toString(); break; | |
case 'b': c = Flotr.Date.monthNames[d.getUTCMonth()]; break; | |
} | |
r.push(c); | |
escape = false; | |
} | |
else { | |
if (c == "%") | |
escape = true; | |
else | |
r.push(c); | |
} | |
} | |
return r.join(""); | |
}, | |
timeUnits: { | |
"second": 1000, | |
"minute": 60 * 1000, | |
"hour": 60 * 60 * 1000, | |
"day": 24 * 60 * 60 * 1000, | |
"month": 30 * 24 * 60 * 60 * 1000, | |
"year": 365.2425 * 24 * 60 * 60 * 1000 | |
}, | |
// the allowed tick sizes, after 1 year we use an integer algorithm | |
spec: [ | |
[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"] | |
}; | |
/* 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; | |
})(); |
/* | |
* 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"); | |
// no canvas, bail out. | |
if (!oCanvas.getContext) { | |
return { | |
saveAsBMP : function(){}, | |
saveAsPNG : function(){}, | |
saveAsJPEG : function(){} | |
} | |
} | |
var bHasImageData = !!(oCanvas.getContext("2d").getImageData); | |
var bHasDataURL = !!(oCanvas.toDataURL); | |
var bHasBase64 = !!(window.btoa); | |
var strDownloadMime = "image/octet-stream"; | |
// ok, we're good | |
var readCanvasData = function(oCanvas) { | |
var iWidth = parseInt(oCanvas.width); | |
var 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 strData = ""; | |
if (typeof data == "string") { | |
strData = data; | |
} else { | |
var aData = data; | |
for (var i = 0; i < aData.length; i++) { | |
strData += String.fromCharCode(aData[i]); | |
} | |
} | |
return btoa(strData); | |
} | |
// creates a base64 encoded string containing BMP data | |
// takes an imagedata object as argument | |
var createBMP = function(oData) { | |
var aHeader = []; | |
var iWidth = oData.width; | |
var iHeight = oData.height; | |
aHeader.push(0x42); // magic 1 | |
aHeader.push(0x4D); | |
var iFileSize = iWidth*iHeight*3 + 54; // total header size = 54 bytes | |
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); | |
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); | |
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256); | |
aHeader.push(iFileSize % 256); | |
aHeader.push(0); // reserved | |
aHeader.push(0); | |
aHeader.push(0); // reserved | |
aHeader.push(0); | |
aHeader.push(54); // data offset | |
aHeader.push(0); | |
aHeader.push(0); | |
aHeader.push(0); | |
var aInfoHeader = []; | |
aInfoHeader.push(40); // info header size | |
aInfoHeader.push(0); | |
aInfoHeader.push(0); | |
aInfoHeader.push(0); | |
var iImageWidth = iWidth; | |
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); | |
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); | |
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256); | |
aInfoHeader.push(iImageWidth % 256); | |
var iImageHeight = iHeight; | |
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); | |
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); | |
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256); | |
aInfoHeader.push(iImageHeight % 256); | |
aInfoHeader.push(1); // num of planes | |
aInfoHeader.push(0); | |
aInfoHeader.push(24); // num of bits per pixel | |
aInfoHeader.push(0); | |
aInfoHeader.push(0); // compression = none | |
aInfoHeader.push(0); | |
aInfoHeader.push(0); | |
aInfoHeader.push(0); | |
var iDataSize = iWidth*iHeight*3; | |
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); | |
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); | |
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256); | |
aInfoHeader.push(iDataSize % 256); | |
for (var i = 0; i < 16; i++) { | |
aInfoHeader.push(0); // these bytes not used | |
} | |
var iPadding = (4 - ((iWidth * 3) % 4)) % 4; | |
var aImgData = oData.data; | |
var strPixelData = ""; | |
var y = iHeight; | |
do { | |
var iOffsetY = iWidth*(y-1)*4; | |
var strPixelRow = ""; | |
for (var x=0;x<iWidth;x++) { | |
var iOffsetX = 4*x; | |
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+2]); | |
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+1]); | |
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX]); | |
} | |
for (var c=0;c<iPadding;c++) { | |
strPixelRow += String.fromCharCode(0); | |
} | |
strPixelData += strPixelRow; | |
} while (--y); | |
return encodeData(aHeader.concat(aInfoHeader)) + encodeData(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); | |
var strData = oScaledCanvas.toDataURL("image/png"); | |
if (bReturnImg) { | |
return makeImageObject(strData); | |
} else { | |
saveFile(strData.replace("image/png", strDownloadMime)); | |
} | |
return true; | |
}, | |
saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) { | |
if (!bHasDataURL) { | |
return false; | |
} | |
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight); | |
var strMime = "image/jpeg"; | |
var 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(strData.replace(strMime, strDownloadMime)); | |
} | |
return true; | |
}, | |
saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) { | |
if (!(bHasImageData && bHasBase64)) { | |
return false; | |
} | |
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight); | |
var oData = readCanvasData(oScaledCanvas); | |
var strImgData = createBMP(oData); | |
if (bReturnImg) { | |
return makeImageObject(makeDataURI(strImgData, "image/bmp")); | |
} else { | |
saveFile(makeDataURI(strImgData, strDownloadMime)); | |
} | |
return true; | |
} | |
}; | |
})(); |
/** | |
* This code is released to the public domain by Jim Studt, 2007. | |
* He may keep some sort of up to date copy at http://www.federated.com/~jim/canvastext/ | |
* A partial support for accentuated letters as been added too. | |
*/ | |
var CanvasText = { | |
/** The letters definition. It is a list of letters, | |
* with their width, and the coordinates of points compositing them. | |
* The syntax for the points is : [x, y], null value means "pen up" | |
*/ | |
letters: { | |
'\n':{ width: -1, points: [] }, | |
' ': { width: 10, points: [] }, | |
'!': { width: 10, points: [[5,21],[5,7],null,[5,2],[4,1],[5,0],[6,1],[5,2]] }, | |
'"': { width: 16, points: [[4,21],[4,14],null,[12,21],[12,14]] }, | |
'#': { width: 21, points: [[11,25],[4,-7],null,[17,25],[10,-7],null,[4,12],[18,12],null,[3,6],[17,6]] }, | |
'$': { width: 20, points: [[8,25],[8,-4],null,[12,25],[12,-4],null,[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, | |
'%': { width: 24, points: [[21,21],[3,0],null,[8,21],[10,19],[10,17],[9,15],[7,14],[5,14],[3,16],[3,18],[4,20],[6,21],[8,21],null,[17,7],[15,6],[14,4],[14,2],[16,0],[18,0],[20,1],[21,3],[21,5],[19,7],[17,7]] }, | |
'&': { width: 26, points: [[23,12],[23,13],[22,14],[21,14],[20,13],[19,11],[17,6],[15,3],[13,1],[11,0],[7,0],[5,1],[4,2],[3,4],[3,6],[4,8],[5,9],[12,13],[13,14],[14,16],[14,18],[13,20],[11,21],[9,20],[8,18],[8,16],[9,13],[11,10],[16,3],[18,1],[20,0],[22,0],[23,1],[23,2]] }, | |
'\'':{ width: 10, points: [[5,19],[4,20],[5,21],[6,20],[6,18],[5,16],[4,15]] }, | |
'(': { width: 14, points: [[11,25],[9,23],[7,20],[5,16],[4,11],[4,7],[5,2],[7,-2],[9,-5],[11,-7]] }, | |
')': { width: 14, points: [[3,25],[5,23],[7,20],[9,16],[10,11],[10,7],[9,2],[7,-2],[5,-5],[3,-7]] }, | |
'*': { width: 16, points: [[8,21],[8,9],null,[3,18],[13,12],null,[13,18],[3,12]] }, | |
'+': { width: 26, points: [[13,18],[13,0],null,[4,9],[22,9]] }, | |
',': { width: 10, points: [[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, | |
'-': { width: 26, points: [[4,9],[22,9]] }, | |
'.': { width: 10, points: [[5,2],[4,1],[5,0],[6,1],[5,2]] }, | |
'/': { width: 22, points: [[20,25],[2,-7]] }, | |
'0': { width: 20, points: [[9,21],[6,20],[4,17],[3,12],[3,9],[4,4],[6,1],[9,0],[11,0],[14,1],[16,4],[17,9],[17,12],[16,17],[14,20],[11,21],[9,21]] }, | |
'1': { width: 20, points: [[6,17],[8,18],[11,21],[11,0]] }, | |
'2': { width: 20, points: [[4,16],[4,17],[5,19],[6,20],[8,21],[12,21],[14,20],[15,19],[16,17],[16,15],[15,13],[13,10],[3,0],[17,0]] }, | |
'3': { width: 20, points: [[5,21],[16,21],[10,13],[13,13],[15,12],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, | |
'4': { width: 20, points: [[13,21],[3,7],[18,7],null,[13,21],[13,0]] }, | |
'5': { width: 20, points: [[15,21],[5,21],[4,12],[5,13],[8,14],[11,14],[14,13],[16,11],[17,8],[17,6],[16,3],[14,1],[11,0],[8,0],[5,1],[4,2],[3,4]] }, | |
'6': { width: 20, points: [[16,18],[15,20],[12,21],[10,21],[7,20],[5,17],[4,12],[4,7],[5,3],[7,1],[10,0],[11,0],[14,1],[16,3],[17,6],[17,7],[16,10],[14,12],[11,13],[10,13],[7,12],[5,10],[4,7]] }, | |
'7': { width: 20, points: [[17,21],[7,0],null,[3,21],[17,21]] }, | |
'8': { width: 20, points: [[8,21],[5,20],[4,18],[4,16],[5,14],[7,13],[11,12],[14,11],[16,9],[17,7],[17,4],[16,2],[15,1],[12,0],[8,0],[5,1],[4,2],[3,4],[3,7],[4,9],[6,11],[9,12],[13,13],[15,14],[16,16],[16,18],[15,20],[12,21],[8,21]] }, | |
'9': { width: 20, points: [[16,14],[15,11],[13,9],[10,8],[9,8],[6,9],[4,11],[3,14],[3,15],[4,18],[6,20],[9,21],[10,21],[13,20],[15,18],[16,14],[16,9],[15,4],[13,1],[10,0],[8,0],[5,1],[4,3]] }, | |
':': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],null,[5,2],[4,1],[5,0],[6,1],[5,2]] }, | |
';': { width: 10, points: [[5,14],[4,13],[5,12],[6,13],[5,14],null,[6,1],[5,0],[4,1],[5,2],[6,1],[6,-1],[5,-3],[4,-4]] }, | |
'<': { width: 24, points: [[20,18],[4,9],[20,0]] }, | |
'=': { width: 26, points: [[4,12],[22,12],null,[4,6],[22,6]] }, | |
'>': { width: 24, points: [[4,18],[20,9],[4,0]] }, | |
'?': { width: 18, points: [[3,16],[3,17],[4,19],[5,20],[7,21],[11,21],[13,20],[14,19],[15,17],[15,15],[14,13],[13,12],[9,10],[9,7],null,[9,2],[8,1],[9,0],[10,1],[9,2]] }, | |
'@': { width: 27, points: [[18,13],[17,15],[15,16],[12,16],[10,15],[9,14],[8,11],[8,8],[9,6],[11,5],[14,5],[16,6],[17,8],null,[12,16],[10,14],[9,11],[9,8],[10,6],[11,5],null,[18,16],[17,8],[17,6],[19,5],[21,5],[23,7],[24,10],[24,12],[23,15],[22,17],[20,19],[18,20],[15,21],[12,21],[9,20],[7,19],[5,17],[4,15],[3,12],[3,9],[4,6],[5,4],[7,2],[9,1],[12,0],[15,0],[18,1],[20,2],[21,3],null,[19,16],[18,8],[18,6],[19,5]] }, | |
'A': { width: 18, points: [[9,21],[1,0],null,[9,21],[17,0],null,[4,7],[14,7]] }, | |
'B': { width: 21, points: [[4,21],[4,0],null,[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],null,[4,11],[13,11],[16,10],[17,9],[18,7],[18,4],[17,2],[16,1],[13,0],[4,0]] }, | |
'C': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5]] }, | |
'D': { width: 21, points: [[4,21],[4,0],null,[4,21],[11,21],[14,20],[16,18],[17,16],[18,13],[18,8],[17,5],[16,3],[14,1],[11,0],[4,0]] }, | |
'E': { width: 19, points: [[4,21],[4,0],null,[4,21],[17,21],null,[4,11],[12,11],null,[4,0],[17,0]] }, | |
'F': { width: 18, points: [[4,21],[4,0],null,[4,21],[17,21],null,[4,11],[12,11]] }, | |
'G': { width: 21, points: [[18,16],[17,18],[15,20],[13,21],[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[18,8],null,[13,8],[18,8]] }, | |
'H': { width: 22, points: [[4,21],[4,0],null,[18,21],[18,0],null,[4,11],[18,11]] }, | |
'I': { width: 8, points: [[4,21],[4,0]] }, | |
'J': { width: 16, points: [[12,21],[12,5],[11,2],[10,1],[8,0],[6,0],[4,1],[3,2],[2,5],[2,7]] }, | |
'K': { width: 21, points: [[4,21],[4,0],null,[18,21],[4,7],null,[9,12],[18,0]] }, | |
'L': { width: 17, points: [[4,21],[4,0],null,[4,0],[16,0]] }, | |
'M': { width: 24, points: [[4,21],[4,0],null,[4,21],[12,0],null,[20,21],[12,0],null,[20,21],[20,0]] }, | |
'N': { width: 22, points: [[4,21],[4,0],null,[4,21],[18,0],null,[18,21],[18,0]] }, | |
'O': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21]] }, | |
'P': { width: 21, points: [[4,21],[4,0],null,[4,21],[13,21],[16,20],[17,19],[18,17],[18,14],[17,12],[16,11],[13,10],[4,10]] }, | |
'Q': { width: 22, points: [[9,21],[7,20],[5,18],[4,16],[3,13],[3,8],[4,5],[5,3],[7,1],[9,0],[13,0],[15,1],[17,3],[18,5],[19,8],[19,13],[18,16],[17,18],[15,20],[13,21],[9,21],null,[12,4],[18,-2]] }, | |
'R': { width: 21, points: [[4,21],[4,0],null,[4,21],[13,21],[16,20],[17,19],[18,17],[18,15],[17,13],[16,12],[13,11],[4,11],null,[11,11],[18,0]] }, | |
'S': { width: 20, points: [[17,18],[15,20],[12,21],[8,21],[5,20],[3,18],[3,16],[4,14],[5,13],[7,12],[13,10],[15,9],[16,8],[17,6],[17,3],[15,1],[12,0],[8,0],[5,1],[3,3]] }, | |
'T': { width: 16, points: [[8,21],[8,0],null,[1,21],[15,21]] }, | |
'U': { width: 22, points: [[4,21],[4,6],[5,3],[7,1],[10,0],[12,0],[15,1],[17,3],[18,6],[18,21]] }, | |
'V': { width: 18, points: [[1,21],[9,0],null,[17,21],[9,0]] }, | |
'W': { width: 24, points: [[2,21],[7,0],null,[12,21],[7,0],null,[12,21],[17,0],null,[22,21],[17,0]] }, | |
'X': { width: 20, points: [[3,21],[17,0],null,[17,21],[3,0]] }, | |
'Y': { width: 18, points: [[1,21],[9,11],[9,0],null,[17,21],[9,11]] }, | |
'Z': { width: 20, points: [[17,21],[3,0],null,[3,21],[17,21],null,[3,0],[17,0]] }, | |
'[': { width: 14, points: [[4,25],[4,-7],null,[5,25],[5,-7],null,[4,25],[11,25],null,[4,-7],[11,-7]] }, | |
'\\':{ width: 14, points: [[0,21],[14,-3]] }, | |
']': { width: 14, points: [[9,25],[9,-7],null,[10,25],[10,-7],null,[3,25],[10,25],null,[3,-7],[10,-7]] }, | |
'^': { width: 14, points: [[3,10],[8,18],[13,10]] }, | |
'_': { width: 16, points: [[0,-2],[16,-2]] }, | |
'`': { width: 10, points: [[6,21],[5,20],[4,18],[4,16],[5,15],[6,16],[5,17]] }, | |
'a': { width: 19, points: [[15,14],[15,0],null,[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'b': { width: 19, points: [[4,21],[4,0],null,[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, | |
'c': { width: 18, points: [[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'd': { width: 19, points: [[15,21],[15,0],null,[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'e': { width: 18, points: [[3,8],[15,8],[15,10],[14,12],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'f': { width: 12, points: [[10,21],[8,21],[6,20],[5,17],[5,0],null,[2,14],[9,14]] }, | |
'g': { width: 19, points: [[15,14],[15,-2],[14,-5],[13,-6],[11,-7],[8,-7],[6,-6],null,[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'h': { width: 19, points: [[4,21],[4,0],null,[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, | |
'i': { width: 8, points: [[3,21],[4,20],[5,21],[4,22],[3,21],null,[4,14],[4,0]] }, | |
'j': { width: 10, points: [[5,21],[6,20],[7,21],[6,22],[5,21],null,[6,14],[6,-3],[5,-6],[3,-7],[1,-7]] }, | |
'k': { width: 17, points: [[4,21],[4,0],null,[14,14],[4,4],null,[8,8],[15,0]] }, | |
'l': { width: 8, points: [[4,21],[4,0]] }, | |
'm': { width: 30, points: [[4,14],[4,0],null,[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0],null,[15,10],[18,13],[20,14],[23,14],[25,13],[26,10],[26,0]] }, | |
'n': { width: 19, points: [[4,14],[4,0],null,[4,10],[7,13],[9,14],[12,14],[14,13],[15,10],[15,0]] }, | |
'o': { width: 19, points: [[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3],[16,6],[16,8],[15,11],[13,13],[11,14],[8,14]] }, | |
'p': { width: 19, points: [[4,14],[4,-7],null,[4,11],[6,13],[8,14],[11,14],[13,13],[15,11],[16,8],[16,6],[15,3],[13,1],[11,0],[8,0],[6,1],[4,3]] }, | |
'q': { width: 19, points: [[15,14],[15,-7],null,[15,11],[13,13],[11,14],[8,14],[6,13],[4,11],[3,8],[3,6],[4,3],[6,1],[8,0],[11,0],[13,1],[15,3]] }, | |
'r': { width: 13, points: [[4,14],[4,0],null,[4,8],[5,11],[7,13],[9,14],[12,14]] }, | |
's': { width: 17, points: [[14,11],[13,13],[10,14],[7,14],[4,13],[3,11],[4,9],[6,8],[11,7],[13,6],[14,4],[14,3],[13,1],[10,0],[7,0],[4,1],[3,3]] }, | |
't': { width: 12, points: [[5,21],[5,4],[6,1],[8,0],[10,0],null,[2,14],[9,14]] }, | |
'u': { width: 19, points: [[4,14],[4,4],[5,1],[7,0],[10,0],[12,1],[15,4],null,[15,14],[15,0]] }, | |
'v': { width: 16, points: [[2,14],[8,0],null,[14,14],[8,0]] }, | |
'w': { width: 22, points: [[3,14],[7,0],null,[11,14],[7,0],null,[11,14],[15,0],null,[19,14],[15,0]] }, | |
'x': { width: 17, points: [[3,14],[14,0],null,[14,14],[3,0]] }, | |
'y': { width: 16, points: [[2,14],[8,0],null,[14,14],[8,0],[6,-4],[4,-6],[2,-7],[1,-7]] }, | |
'z': { width: 17, points: [[14,14],[3,0],null,[3,14],[14,14],null,[3,0],[14,0]] }, | |
'{': { width: 14, points: [[9,25],[7,24],[6,23],[5,21],[5,19],[6,17],[7,16],[8,14],[8,12],[6,10],null,[7,24],[6,22],[6,20],[7,18],[8,17],[9,15],[9,13],[8,11],[4,9],[8,7],[9,5],[9,3],[8,1],[7,0],[6,-2],[6,-4],[7,-6],null,[6,8],[8,6],[8,4],[7,2],[6,1],[5,-1],[5,-3],[6,-5],[7,-6],[9,-7]] }, | |
'|': { width: 8, points: [[4,25],[4,-7]] }, | |
'}': { width: 14, points: [[5,25],[7,24],[8,23],[9,21],[9,19],[8,17],[7,16],[6,14],[6,12],[8,10],null,[7,24],[8,22],[8,20],[7,18],[6,17],[5,15],[5,13],[6,11],[10,9],[6,7],[5,5],[5,3],[6,1],[7,0],[8,-2],[8,-4],[7,-6],null,[8,8],[6,6],[6,4],[7,2],[8,1],[9,-1],[9,-3],[8,-5],[7,-6],[5,-7]] }, | |
'~': { width: 24, points: [[3,6],[3,8],[4,11],[6,12],[8,12],[10,11],[14,8],[16,7],[18,7],[20,8],[21,10],null,[3,8],[4,10],[6,11],[8,11],[10,10],[14,7],[16,6],[18,6],[20,7],[21,10],[21,12]] }, | |
}, | |
specialchars: { | |
'pi': { width: 19, points: [[6,14],[6,0],null,[14,14],[14,0],null,[2,13],[6,16],[13,13],[17,16]] } | |
}, | |
/** Diacritics, used to draw accentuated letters */ | |
diacritics: { | |
'`': { entity: 'grave', points: [[7,22],[12,19]] }, | |
'^': { entity: 'circ', points: [[5.5,19],[9.5,23],[12.5,19]] }, | |
'~': { entity: 'tilde', points: [[4,18],[7,22],[10,18],[13,22]] } | |
}, | |
/** The default font styling */ | |
style: { | |
size: 8, // font height in pixels | |
font: null, // not yet implemented | |
color: '#000000', // | |
weight: 1, // float, 1 for 'normal' | |
halign: 'l', // l: left, r: right, c: center | |
valign: 'b', // t: top, m: middle, b: bottom | |
adjustAlign: false, // modifies the alignments if the angle is different from 0 to make the spin point always at the good position | |
angle: 0, // in radians, anticlockwise | |
tracking: 1, // space between the letters, float, 1 for 'normal' | |
boundingBoxColor: '#ff0000', //null // color of the bounding box (null to hide), can be used for debug and font drawing | |
originPointColor: '#000000' //null // color of the bounding box (null to hide), can be used for debug and font drawing | |
}, | |
debug: false, | |
_bufferLexemes: {}, | |
/** Get the letter data corresponding to a char | |
* @param {String} ch - The char | |
*/ | |
letter: function(ch) { | |
return CanvasText.letters[ch]; | |
}, | |
parseLexemes: function(str) { | |
if (CanvasText._bufferLexemes[str]) | |
return CanvasText._bufferLexemes[str]; | |
var i, c, matches = str.match(/&[A-Za-z]{2,5};|\s|./g); | |
var result = [], chars = []; | |
for (i = 0; i < matches.length; i++) { | |
c = matches[i]; | |
if (c.length == 1) | |
chars.push(c); | |
else { | |
var entity = c.substring(1, c.length-1); | |
if (CanvasText.specialchars[entity]) | |
chars.push(entity); | |
else | |
chars = chars.concat(c.toArray()); | |
} | |
} | |
for (i = 0; i < chars.length; i++) { | |
c = chars[i]; | |
if (c = CanvasText.letters[c] || CanvasText.specialchars[c]) | |
result.push(c); | |
} | |
return CanvasText._bufferLexemes[str] = result.compact(); | |
}, | |
/** Get the font ascent for a given style | |
* @param {Object} style - The reference style | |
*/ | |
ascent: function(style) { | |
style = style || {}; | |
return (style.size || CanvasText.style.size); | |
}, | |
/** Get the font descent for a given style | |
* @param {Object} style - The reference style | |
* */ | |
descent: function(style) { | |
style = style || {}; | |
return 7.0*(style.size || CanvasText.style.size)/25.0; | |
}, | |
/** Measure the text horizontal size | |
* @param {String} str - The text | |
* @param {Object} style - Text style | |
* */ | |
measure: function(str, style) { | |
if (!str) return; | |
style = style || {}; | |
var i, width, lexemes = CanvasText.parseLexemes(str), | |
total = 0; | |
for (i = lexemes.length-1; i > -1; --i) { | |
c = lexemes[i]; | |
width = (c.diacritic) ? CanvasText.letter(c.letter).width : c.width; | |
total += width * (style.tracking || CanvasText.style.tracking) * (style.size || CanvasText.style.size) / 25.0; | |
} | |
return total; | |
}, | |
getDimensions: function(str, style) { | |
var width = CanvasText.measure(str, style), | |
height = style.size || CanvasText.style.size, | |
angle = style.angle || CanvasText.style.angle; | |
if (style.angle == 0) return {width: width, height: height}; | |
return { | |
width: Math.abs(Math.cos(angle) * width) + Math.abs(Math.sin(angle) * height), | |
height: Math.abs(Math.sin(angle) * width) + Math.abs(Math.cos(angle) * height) | |
} | |
}, | |
getBestAlign: function(angle, style) { | |
angle += CanvasText.getAngleFromAlign(style.halign, style.valign); | |
var a = {h:'c', v:'m'}; | |
if (Math.round(Math.cos(angle)*1000)/1000 != 0) | |
a.h = (Math.cos(angle) > 0 ? 'r' : 'l'); | |
if (Math.round(Math.sin(angle)*1000)/1000 != 0) | |
a.v = (Math.sin(angle) > 0 ? 't' : 'b'); | |
return a; | |
}, | |
getAngleFromAlign: function(halign, valign) { | |
var pi = Math.PI, table = { | |
'rm': 0, | |
'rt': pi/4, | |
'ct': pi/2, | |
'lt': 3*(pi/4), | |
'lm': pi, | |
'lb': -3*(pi/4), | |
'cb': -pi/2, | |
'rb': -pi/4, | |
'cm': 0 | |
} | |
return table[halign+valign]; | |
}, | |
/** Draws serie of points at given coordinates | |
* @param {Canvas context} ctx - The canvas context | |
* @param {Array} points - The points to draw | |
* @param {Number} x - The X coordinate | |
* @param {Number} y - The Y coordinate | |
* @param {Number} mag - The scale | |
*/ | |
drawPoints: function (ctx, points, x, y, mag, offset) { | |
var i, a, penUp = true, needStroke = 0; | |
offset = offset || {x:0, y:0}; | |
ctx.beginPath(); | |
for (i = 0; i < points.length; i++) { | |
a = points[i]; | |
if (!a) { | |
penUp = true; | |
continue; | |
} | |
if (penUp) { | |
ctx.moveTo(x + a[0]*mag + offset.x, y - a[1]*mag + offset.y); | |
penUp = false; | |
} | |
else { | |
ctx.lineTo(x + a[0]*mag + offset.x, y - a[1]*mag + offset.y); | |
} | |
} | |
ctx.stroke(); | |
}, | |
/** Draws a text at given coordinates and with a given style | |
* @param {Canvas context} ctx - The canvas context | |
* @param {String} str - The text to draw | |
* @param {Number} xOrig - The X coordinate | |
* @param {Number} yOrig - The Y coordinate | |
* @param {Object} style - The font style | |
*/ | |
draw: function(ctx, str, xOrig, yOrig, style) { | |
if (!str) return; | |
style = style || CanvasText.style; | |
style.halign = style.halign || CanvasText.style.halign; | |
style.valign = style.valign || CanvasText.style.valign; | |
style.angle = style.angle || CanvasText.style.angle; | |
style.size = style.size || CanvasText.style.size; | |
style.adjustAlign = style.adjustAlign || CanvasText.style.adjustAlign; | |
var i, c, total = 0, | |
mag = style.size / 25.0, | |
x = 0, y = 0, | |
lexemes = CanvasText.parseLexemes(str); | |
var offset = {x:0, y:0}, | |
measure = CanvasText.measure(str, style), | |
align; | |
if (style.adjustAlign) { | |
align = CanvasText.getBestAlign(style.angle, style); | |
style.halign = align.h; | |
style.valign = align.v; | |
} | |
switch (style.halign) { | |
case 'l': break; | |
case 'c': offset.x = -measure / 2; break; | |
case 'r': offset.x = -measure; break; | |
} | |
switch (style.valign) { | |
case 'b': break; | |
case 'm': offset.y = style.size / 2; break; | |
case 't': offset.y = style.size; break; | |
} | |
ctx.save(); | |
ctx.translate(xOrig, yOrig); | |
ctx.rotate(style.angle); | |
ctx.lineCap = "round"; | |
ctx.lineWidth = 2.0 * mag * (style.weight || CanvasText.style.weight); | |
ctx.strokeStyle = style.color || CanvasText.style.color; | |
for (i = 0; i < lexemes.length; i++) { | |
c = lexemes[i]; | |
if (c.width == -1) { | |
x = 0; | |
y = style.size * 1.4; | |
continue; | |
} | |
var points = c.points, | |
width = c.width; | |
if (c.diacritic) { | |
var dia = CanvasText.diacritics[c.diacritic]; | |
var char = CanvasText.letter(c.letter); | |
CanvasText.drawPoints(ctx, dia.points, x, y - (c.letter.toUpperCase() == c.letter ? 3 : 0), mag, offset); | |
points = char.points; | |
width = char.width; | |
} | |
CanvasText.drawPoints(ctx, points, x, y, mag, offset); | |
if (CanvasText.debug) { | |
ctx.save(); | |
ctx.lineJoin = "miter"; | |
ctx.lineWidth = 0.5; | |
ctx.strokeStyle = (style.boundingBoxColor || CanvasText.style.boundingBoxColor); | |
ctx.strokeRect(x+offset.x, y+offset.y, width*mag, -style.size); | |
ctx.fillStyle = (style.originPointColor || CanvasText.style.originPointColor); | |
ctx.beginPath(); | |
ctx.arc(0, 0, 1.5, 0, Math.PI*2, true); | |
ctx.fill(); | |
ctx.restore(); | |
} | |
x += width*mag*(style.tracking || CanvasText.style.tracking); | |
} | |
ctx.restore(); | |
return total; | |
}, | |
/** Enables the text function for a Canvas context | |
* @param {Canvas context} ctx - The canvas context | |
*/ | |
enable: function(ctx) { | |
ctx.drawText = function(text, x, y, style) { return CanvasText.draw(ctx, text, x, y, style); }; | |
ctx.measureText = function(text, style) { return CanvasText.measure(text, style); }; | |
ctx.getTextBounds = function(text, style) { return CanvasText.getDimensions(text, style); }; | |
ctx.fontAscent = function(style) { return CanvasText.ascent(style); }; | |
ctx.fontDescent = function(style) { return CanvasText.descent(style); }; | |
} | |
}; |
// Copyright 2006 Google Inc. | |
// | |
// 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. | |
// Known Issues: | |
// | |
// * Patterns are not implemented. | |
// * Radial gradient are not implemented. The VML version of these look very | |
// different from the canvas one. | |
// * Clipping paths are not implemented. | |
// * Coordsize. The width and height attribute have higher priority than the | |
// width and height style values which isn't correct. | |
// * Painting mode isn't implemented. | |
// * Canvas width/height should is using content-box by default. IE in | |
// Quirks mode will draw the canvas using border-box. Either change your | |
// doctype to HTML5 | |
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype) | |
// or use Box Sizing Behavior from WebFX | |
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html) | |
// * Non uniform scaling does not correctly scale strokes. | |
// * Optimize. There is always room for speed improvements. | |
// Only add this code if we do not already have a canvas implementation | |
if (!document.createElement('canvas').getContext) { | |
(function() { | |
// alias some functions to make (compiled) code shorter | |
var m = Math; | |
var mr = m.round; | |
var ms = m.sin; | |
var mc = m.cos; | |
var abs = m.abs; | |
var sqrt = m.sqrt; | |
// this is used for sub pixel precision | |
var Z = 10; | |
var Z2 = Z / 2; | |
/** | |
* This funtion is assigned to the <canvas> elements as element.getContext(). | |
* @this {HTMLElement} | |
* @return {CanvasRenderingContext2D_} | |
*/ | |
function getContext() { | |
return this.context_ || | |
(this.context_ = new CanvasRenderingContext2D_(this)); | |
} | |
var slice = Array.prototype.slice; | |
/** | |
* Binds a function to an object. The returned function will always use the | |
* passed in {@code obj} as {@code this}. | |
* | |
* Example: | |
* | |
* g = bind(f, obj, a, b) | |
* g(c, d) // will do f.call(obj, a, b, c, d) | |
* | |
* @param {Function} f The function to bind the object to | |
* @param {Object} obj The object that should act as this when the function | |
* is called | |
* @param {*} var_args Rest arguments that will be used as the initial | |
* arguments when the function is called | |
* @return {Function} A new function that has bound this | |
*/ | |
function bind(f, obj, var_args) { | |
var a = slice.call(arguments, 2); | |
return function() { | |
return f.apply(obj, a.concat(slice.call(arguments))); | |
}; | |
} | |
var G_vmlCanvasManager_ = { | |
init: function(opt_doc) { | |
if (/MSIE/.test(navigator.userAgent) && !window.opera) { | |
var doc = opt_doc || document; | |
// Create a dummy element so that IE will allow canvas elements to be | |
// recognized. | |
doc.createElement('canvas'); | |
doc.attachEvent('onreadystatechange', bind(this.init_, this, doc)); | |
} | |
}, | |
init_: function(doc) { | |
// create xmlns | |
if (!doc.namespaces['g_vml_']) { | |
doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml', | |
'#default#VML'); | |
} | |
if (!doc.namespaces['g_o_']) { | |
doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office', | |
'#default#VML'); | |
} | |
// Setup default CSS. Only add one style sheet per document | |
if (!doc.styleSheets['ex_canvas_']) { | |
var ss = doc.createStyleSheet(); | |
ss.owningElement.id = 'ex_canvas_'; | |
ss.cssText = 'canvas{display:inline-block;overflow:hidden;' + | |
// default size is 300x150 in Gecko and Opera | |
'text-align:left;width:300px;height:150px}' + | |
'g_vml_\\:*{behavior:url(#default#VML)}' + | |
'g_o_\\:*{behavior:url(#default#VML)}'; | |
} | |
// find all canvas elements | |
var els = doc.getElementsByTagName('canvas'); | |
for (var i = 0; i < els.length; i++) { | |
this.initElement(els[i]); | |
} | |
}, | |
/** | |
* Public initializes a canvas element so that it can be used as canvas | |
* element from now on. This is called automatically before the page is | |
* loaded but if you are creating elements using createElement you need to | |
* make sure this is called on the element. | |
* @param {HTMLElement} el The canvas element to initialize. | |
* @return {HTMLElement} the element that was created. | |
*/ | |
initElement: function(el) { | |
if (!el.getContext) { | |
el.getContext = getContext; | |
// Remove fallback content. There is no way to hide text nodes so we | |
// just remove all childNodes. We could hide all elements and remove | |
// text nodes but who really cares about the fallback content. | |
el.innerHTML = ''; | |
// do not use inline function because that will leak memory | |
el.attachEvent('onpropertychange', onPropertyChange); | |
el.attachEvent('onresize', onResize); | |
var attrs = el.attributes; | |
if (attrs.width && attrs.width.specified) { | |
// TODO: use runtimeStyle and coordsize | |
// el.getContext().setWidth_(attrs.width.nodeValue); | |
el.style.width = attrs.width.nodeValue + 'px'; | |
} else { | |
el.width = el.clientWidth; | |
} | |
if (attrs.height && attrs.height.specified) { | |
// TODO: use runtimeStyle and coordsize | |
// el.getContext().setHeight_(attrs.height.nodeValue); | |
el.style.height = attrs.height.nodeValue + 'px'; | |
} else { | |
el.height = el.clientHeight; | |
} | |
//el.getContext().setCoordsize_() | |
} | |
return el; | |
} | |
}; | |
function onPropertyChange(e) { | |
var el = e.srcElement; | |
switch (e.propertyName) { | |
case 'width': | |
el.style.width = el.attributes.width.nodeValue + 'px'; | |
el.getContext().clearRect(); | |
break; | |
case 'height': | |
el.style.height = el.attributes.height.nodeValue + 'px'; | |
el.getContext().clearRect(); | |
break; | |
} | |
} | |
function onResize(e) { | |
var el = e.srcElement; | |
if (el.firstChild) { | |
el.firstChild.style.width = el.clientWidth + 'px'; | |
el.firstChild.style.height = el.clientHeight + 'px'; | |
} | |
} | |
G_vmlCanvasManager_.init(); | |
// precompute "00" to "FF" | |
var dec2hex = []; | |
for (var i = 0; i < 16; i++) { | |
for (var j = 0; j < 16; j++) { | |
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16); | |
} | |
} | |
function createMatrixIdentity() { | |
return [ | |
[1, 0, 0], | |
[0, 1, 0], | |
[0, 0, 1] | |
]; | |
} | |
function matrixMultiply(m1, m2) { | |
var result = createMatrixIdentity(); | |
for (var x = 0; x < 3; x++) { | |
for (var y = 0; y < 3; y++) { | |
var sum = 0; | |
for (var z = 0; z < 3; z++) { | |
sum += m1[x][z] * m2[z][y]; | |
} | |
result[x][y] = sum; | |
} | |
} | |
return result; | |
} | |
function copyState(o1, o2) { | |
o2.fillStyle = o1.fillStyle; | |
o2.lineCap = o1.lineCap; | |
o2.lineJoin = o1.lineJoin; | |
o2.lineWidth = o1.lineWidth; | |
o2.miterLimit = o1.miterLimit; | |
o2.shadowBlur = o1.shadowBlur; | |
o2.shadowColor = o1.shadowColor; | |
o2.shadowOffsetX = o1.shadowOffsetX; | |
o2.shadowOffsetY = o1.shadowOffsetY; | |
o2.strokeStyle = o1.strokeStyle; | |
o2.globalAlpha = o1.globalAlpha; | |
o2.arcScaleX_ = o1.arcScaleX_; | |
o2.arcScaleY_ = o1.arcScaleY_; | |
o2.lineScale_ = o1.lineScale_; | |
} | |
function processStyle(styleString) { | |
var str, alpha = 1; | |
styleString = String(styleString); | |
if (styleString.substring(0, 3) == 'rgb') { | |
var start = styleString.indexOf('(', 3); | |
var end = styleString.indexOf(')', start + 1); | |
var guts = styleString.substring(start + 1, end).split(','); | |
str = '#'; | |
for (var i = 0; i < 3; i++) { | |
str += dec2hex[Number(guts[i])]; | |
} | |
if (guts.length == 4 && styleString.substr(3, 1) == 'a') { | |
alpha = guts[3]; | |
} | |
} else { | |
str = styleString; | |
} | |
return {color: str, alpha: alpha}; | |
} | |
function processLineCap(lineCap) { | |
switch (lineCap) { | |
case 'butt': | |
return 'flat'; | |
case 'round': | |
return 'round'; | |
case 'square': | |
default: | |
return 'square'; | |
} | |
} | |
/** | |
* This class implements CanvasRenderingContext2D interface as described by | |
* the WHATWG. | |
* @param {HTMLElement} surfaceElement The element that the 2D context should | |
* be associated with | |
*/ | |
function CanvasRenderingContext2D_(surfaceElement) { | |
this.m_ = createMatrixIdentity(); | |
this.mStack_ = []; | |
this.aStack_ = []; | |
this.currentPath_ = []; | |
// Canvas context properties | |
this.strokeStyle = '#000'; | |
this.fillStyle = '#000'; | |
this.lineWidth = 1; | |
this.lineJoin = 'miter'; | |
this.lineCap = 'butt'; | |
this.miterLimit = Z * 1; | |
this.globalAlpha = 1; | |
this.canvas = surfaceElement; | |
var el = surfaceElement.ownerDocument.createElement('div'); | |
el.style.width = surfaceElement.clientWidth + 'px'; | |
el.style.height = surfaceElement.clientHeight + 'px'; | |
el.style.overflow = 'hidden'; | |
el.style.position = 'absolute'; | |
surfaceElement.appendChild(el); | |
this.element_ = el; | |
this.arcScaleX_ = 1; | |
this.arcScaleY_ = 1; | |
this.lineScale_ = 1; | |
} | |
var contextPrototype = CanvasRenderingContext2D_.prototype; | |
contextPrototype.clearRect = function() { | |
this.element_.innerHTML = ''; | |
}; | |
contextPrototype.beginPath = function() { | |
// TODO: Branch current matrix so that save/restore has no effect | |
// as per safari docs. | |
this.currentPath_ = []; | |
}; | |
contextPrototype.moveTo = function(aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y}); | |
this.currentX_ = p.x; | |
this.currentY_ = p.y; | |
}; | |
contextPrototype.lineTo = function(aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y}); | |
this.currentX_ = p.x; | |
this.currentY_ = p.y; | |
}; | |
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y, | |
aCP2x, aCP2y, | |
aX, aY) { | |
var p = this.getCoords_(aX, aY); | |
var cp1 = this.getCoords_(aCP1x, aCP1y); | |
var cp2 = this.getCoords_(aCP2x, aCP2y); | |
bezierCurveTo(this, cp1, cp2, p); | |
}; | |
// Helper function that takes the already fixed cordinates. | |
function bezierCurveTo(self, cp1, cp2, p) { | |
self.currentPath_.push({ | |
type: 'bezierCurveTo', | |
cp1x: cp1.x, | |
cp1y: cp1.y, | |
cp2x: cp2.x, | |
cp2y: cp2.y, | |
x: p.x, | |
y: p.y | |
}); | |
self.currentX_ = p.x; | |
self.currentY_ = p.y; | |
} | |
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) { | |
// the following is lifted almost directly from | |
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes | |
var cp = this.getCoords_(aCPx, aCPy); | |
var p = this.getCoords_(aX, aY); | |
var cp1 = { | |
x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_), | |
y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_) | |
}; | |
var cp2 = { | |
x: cp1.x + (p.x - this.currentX_) / 3.0, | |
y: cp1.y + (p.y - this.currentY_) / 3.0 | |
}; | |
bezierCurveTo(this, cp1, cp2, p); | |
}; | |
contextPrototype.arc = function(aX, aY, aRadius, | |
aStartAngle, aEndAngle, aClockwise) { | |
aRadius *= Z; | |
var arcType = aClockwise ? 'at' : 'wa'; | |
var xStart = aX + mc(aStartAngle) * aRadius - Z2; | |
var yStart = aY + ms(aStartAngle) * aRadius - Z2; | |
var xEnd = aX + mc(aEndAngle) * aRadius - Z2; | |
var yEnd = aY + ms(aEndAngle) * aRadius - Z2; | |
// IE won't render arches drawn counter clockwise if xStart == xEnd. | |
if (xStart == xEnd && !aClockwise) { | |
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something | |
// that can be represented in binary | |
} | |
var p = this.getCoords_(aX, aY); | |
var pStart = this.getCoords_(xStart, yStart); | |
var pEnd = this.getCoords_(xEnd, yEnd); | |
this.currentPath_.push({type: arcType, | |
x: p.x, | |
y: p.y, | |
radius: aRadius, | |
xStart: pStart.x, | |
yStart: pStart.y, | |
xEnd: pEnd.x, | |
yEnd: pEnd.y}); | |
}; | |
contextPrototype.rect = function(aX, aY, aWidth, aHeight) { | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
}; | |
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) { | |
var oldPath = this.currentPath_; | |
this.beginPath(); | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
this.stroke(); | |
this.currentPath_ = oldPath; | |
}; | |
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) { | |
var oldPath = this.currentPath_; | |
this.beginPath(); | |
this.moveTo(aX, aY); | |
this.lineTo(aX + aWidth, aY); | |
this.lineTo(aX + aWidth, aY + aHeight); | |
this.lineTo(aX, aY + aHeight); | |
this.closePath(); | |
this.fill(); | |
this.currentPath_ = oldPath; | |
}; | |
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) { | |
var gradient = new CanvasGradient_('gradient'); | |
gradient.x0_ = aX0; | |
gradient.y0_ = aY0; | |
gradient.x1_ = aX1; | |
gradient.y1_ = aY1; | |
return gradient; | |
}; | |
contextPrototype.createRadialGradient = function(aX0, aY0, aR0, | |
aX1, aY1, aR1) { | |
var gradient = new CanvasGradient_('gradientradial'); | |
gradient.x0_ = aX0; | |
gradient.y0_ = aY0; | |
gradient.r0_ = aR0; | |
gradient.x1_ = aX1; | |
gradient.y1_ = aY1; | |
gradient.r1_ = aR1; | |
return gradient; | |
}; | |
contextPrototype.drawImage = function(image, var_args) { | |
var dx, dy, dw, dh, sx, sy, sw, sh; | |
// to find the original width we overide the width and height | |
var oldRuntimeWidth = image.runtimeStyle.width; | |
var oldRuntimeHeight = image.runtimeStyle.height; | |
image.runtimeStyle.width = 'auto'; | |
image.runtimeStyle.height = 'auto'; | |
// get the original size | |
var w = image.width; | |
var h = image.height; | |
// and remove overides | |
image.runtimeStyle.width = oldRuntimeWidth; | |
image.runtimeStyle.height = oldRuntimeHeight; | |
if (arguments.length == 3) { | |
dx = arguments[1]; | |
dy = arguments[2]; | |
sx = sy = 0; | |
sw = dw = w; | |
sh = dh = h; | |
} else if (arguments.length == 5) { | |
dx = arguments[1]; | |
dy = arguments[2]; | |
dw = arguments[3]; | |
dh = arguments[4]; | |
sx = sy = 0; | |
sw = w; | |
sh = h; | |
} else if (arguments.length == 9) { | |
sx = arguments[1]; | |
sy = arguments[2]; | |
sw = arguments[3]; | |
sh = arguments[4]; | |
dx = arguments[5]; | |
dy = arguments[6]; | |
dw = arguments[7]; | |
dh = arguments[8]; | |
} else { | |
throw Error('Invalid number of arguments'); | |
} | |
var d = this.getCoords_(dx, dy); | |
var w2 = sw / 2; | |
var h2 = sh / 2; | |
var vmlStr = []; | |
var W = 10; | |
var H = 10; | |
// For some reason that I've now forgotten, using divs didn't work | |
vmlStr.push(' <g_vml_:group', | |
' coordsize="', Z * W, ',', Z * H, '"', | |
' coordorigin="0,0"' , | |
' style="width:', W, 'px;height:', H, 'px;position:absolute;'); | |
// If filters are necessary (rotation exists), create them | |
// filters are bog-slow, so only create them if abbsolutely necessary | |
// The following check doesn't account for skews (which don't exist | |
// in the canvas spec (yet) anyway. | |
if (this.m_[0][0] != 1 || this.m_[0][1]) { | |
var filter = []; | |
// Note the 12/21 reversal | |
filter.push('M11=', this.m_[0][0], ',', | |
'M12=', this.m_[1][0], ',', | |
'M21=', this.m_[0][1], ',', | |
'M22=', this.m_[1][1], ',', | |
'Dx=', mr(d.x / Z), ',', | |
'Dy=', mr(d.y / Z), ''); | |
// Bounding box calculation (need to minimize displayed area so that | |
// filters don't waste time on unused pixels. | |
var max = d; | |
var c2 = this.getCoords_(dx + dw, dy); | |
var c3 = this.getCoords_(dx, dy + dh); | |
var c4 = this.getCoords_(dx + dw, dy + dh); | |
max.x = m.max(max.x, c2.x, c3.x, c4.x); | |
max.y = m.max(max.y, c2.y, c3.y, c4.y); | |
vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z), | |
'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(', | |
filter.join(''), ", sizingmethod='clip');") | |
} else { | |
vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;'); | |
} | |
vmlStr.push(' ">' , | |
'<g_vml_:image src="', image.src, '"', | |
' style="width:', Z * dw, 'px;', | |
' height:', Z * dh, 'px;"', | |
' cropleft="', sx / w, '"', | |
' croptop="', sy / h, '"', | |
' cropright="', (w - sx - sw) / w, '"', | |
' cropbottom="', (h - sy - sh) / h, '"', | |
' />', | |
'</g_vml_:group>'); | |
this.element_.insertAdjacentHTML('BeforeEnd', | |
vmlStr.join('')); | |
}; | |
contextPrototype.stroke = function(aFill) { | |
var lineStr = []; | |
var lineOpen = false; | |
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle); | |
var color = a.color; | |
var opacity = a.alpha * this.globalAlpha; | |
var W = 10; | |
var H = 10; | |
lineStr.push('<g_vml_:shape', | |
' filled="', !!aFill, '"', | |
' style="position:absolute;width:', W, 'px;height:', H, 'px;"', | |
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"', | |
' stroked="', !aFill, '"', | |
' path="'); | |
var newSeq = false; | |
var min = {x: null, y: null}; | |
var max = {x: null, y: null}; | |
for (var i = 0; i < this.currentPath_.length; i++) { | |
var p = this.currentPath_[i]; | |
var c; | |
switch (p.type) { | |
case 'moveTo': | |
c = p; | |
lineStr.push(' m ', mr(p.x), ',', mr(p.y)); | |
break; | |
case 'lineTo': | |
lineStr.push(' l ', mr(p.x), ',', mr(p.y)); | |
break; | |
case 'close': | |
lineStr.push(' x '); | |
p = null; | |
break; | |
case 'bezierCurveTo': | |
lineStr.push(' c ', | |
mr(p.cp1x), ',', mr(p.cp1y), ',', | |
mr(p.cp2x), ',', mr(p.cp2y), ',', | |
mr(p.x), ',', mr(p.y)); | |
break; | |
case 'at': | |
case 'wa': | |
lineStr.push(' ', p.type, ' ', | |
mr(p.x - this.arcScaleX_ * p.radius), ',', | |
mr(p.y - this.arcScaleY_ * p.radius), ' ', | |
mr(p.x + this.arcScaleX_ * p.radius), ',', | |
mr(p.y + this.arcScaleY_ * p.radius), ' ', | |
mr(p.xStart), ',', mr(p.yStart), ' ', | |
mr(p.xEnd), ',', mr(p.yEnd)); | |
break; | |
} | |
// TODO: Following is broken for curves due to | |
// move to proper paths. | |
// Figure out dimensions so we can do gradient fills | |
// properly | |
if (p) { | |
if (min.x == null || p.x < min.x) { | |
min.x = p.x; | |
} | |
if (max.x == null || p.x > max.x) { | |
max.x = p.x; | |
} | |
if (min.y == null || p.y < min.y) { | |
min.y = p.y; | |
} | |
if (max.y == null || p.y > max.y) { | |
max.y = p.y; | |
} | |
} | |
} | |
lineStr.push(' ">'); | |
if (!aFill) { | |
var lineWidth = this.lineScale_ * this.lineWidth; | |
// VML cannot correctly render a line if the width is less than 1px. | |
// In that case, we dilute the color to make the line look thinner. | |
if (lineWidth < 1) { | |
opacity *= lineWidth; | |
} | |
lineStr.push( | |
'<g_vml_:stroke', | |
' opacity="', opacity, '"', | |
' joinstyle="', this.lineJoin, '"', | |
' miterlimit="', this.miterLimit, '"', | |
' endcap="', processLineCap(this.lineCap), '"', | |
' weight="', lineWidth, 'px"', | |
' color="', color, '" />' | |
); | |
} else if (typeof this.fillStyle == 'object') { | |
var fillStyle = this.fillStyle; | |
var angle = 0; | |
var focus = {x: 0, y: 0}; | |
// additional offset | |
var shift = 0; | |
// scale factor for offset | |
var expansion = 1; | |
if (fillStyle.type_ == 'gradient') { | |
var x0 = fillStyle.x0_ / this.arcScaleX_; | |
var y0 = fillStyle.y0_ / this.arcScaleY_; | |
var x1 = fillStyle.x1_ / this.arcScaleX_; | |
var y1 = fillStyle.y1_ / this.arcScaleY_; | |
var p0 = this.getCoords_(x0, y0); | |
var p1 = this.getCoords_(x1, y1); | |
var dx = p1.x - p0.x; | |
var dy = p1.y - p0.y; | |
angle = Math.atan2(dx, dy) * 180 / Math.PI; | |
// The angle should be a non-negative number. | |
if (angle < 0) { | |
angle += 360; | |
} | |
// Very small angles produce an unexpected result because they are | |
// converted to a scientific notation string. | |
if (angle < 1e-6) { | |
angle = 0; | |
} | |
} else { | |
var p0 = this.getCoords_(fillStyle.x0_, fillStyle.y0_); | |
var width = max.x - min.x; | |
var height = max.y - min.y; | |
focus = { | |
x: (p0.x - min.x) / width, | |
y: (p0.y - min.y) / height | |
}; | |
width /= this.arcScaleX_ * Z; | |
height /= this.arcScaleY_ * Z; | |
var dimension = m.max(width, height); | |
shift = 2 * fillStyle.r0_ / dimension; | |
expansion = 2 * fillStyle.r1_ / dimension - shift; | |
} | |
// We need to sort the color stops in ascending order by offset, | |
// otherwise IE won't interpret it correctly. | |
var stops = fillStyle.colors_; | |
stops.sort(function(cs1, cs2) { | |
return cs1.offset - cs2.offset; | |
}); | |
var length = stops.length; | |
var color1 = stops[0].color; | |
var color2 = stops[length - 1].color; | |
var opacity1 = stops[0].alpha * this.globalAlpha; | |
var opacity2 = stops[length - 1].alpha * this.globalAlpha; | |
var colors = []; | |
for (var i = 0; i < length; i++) { | |
var stop = stops[i]; | |
colors.push(stop.offset * expansion + shift + ' ' + stop.color); | |
} | |
// When colors attribute is used, the meanings of opacity and o:opacity2 | |
// are reversed. | |
lineStr.push('&l |