diff -Nru openplacos-0.3+560~lucid1/components/pid-dutycycle-regulation.rb openplacos-0.4+554~ubuntu10.04.1/components/pid-dutycycle-regulation.rb --- openplacos-0.3+560~lucid1/components/pid-dutycycle-regulation.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/components/pid-dutycycle-regulation.rb 2015-01-29 17:00:42.000000000 +0000 @@ -12,6 +12,9 @@ c.option :proportional, 'Proportional gain', :default => 0.01 c.option :differential, 'Differential gain', :default => 0.005 c.option :integrative, 'Integrative gain' , :default => 0.005 + c.option :dividor, 'Gain dividor. Divide all gains by this factor since cant pass float' , :default => 1 + c.option :initial_value, 'Set regulation active on start and set consign to this value' + c.option :start_at_startup, 'Start regulation at startup', :default => false end @@ -21,7 +24,7 @@ attr_accessor :frequency, :threshold ,:hysteresis, :pidcontroller attr_reader :is_regul_on - def initialize(type_,frequency_,sensor_,actuator_, kp_, ki_, kd_) + def initialize(type_,frequency_,sensor_,actuator_, kp_, ki_, kd_, dividor_) @is_regul_on = false @threshold = nil @@ -31,9 +34,9 @@ @sensor = sensor_ @actuator = actuator_ - @kp = kp_ # Proportional gain - @ki = ki_ # Integrative gain - @kd = kd_ # Derivative gain + @kp = kp_/dividor_ # Proportional gain + @ki = ki_/dividor_ # Integrative gain + @kd = kd_/dividor_ # Derivative gain @command = 0 @@ -124,7 +127,6 @@ end def write_actuator(value) - puts "write: " + value.to_s @actuator.write(value,{}) if @actuator.read({}) != value end @@ -143,8 +145,19 @@ component << consign = LibComponent::Input.new("/regul","analog.regul.consign") component << frequency = LibComponent::Input.new("/regul","analog.regul.frequency") -regul = Regulation.new(component.options[:actuator], component.options[:frequency], sensor,actuator, component.options[:proportional], component.options[:integrative], component.options[:differential] ) -regul.pidcontroller.set_consign(0) +regul = Regulation.new(component.options[:actuator], + component.options[:frequency], + sensor, + actuator, + component.options[:proportional], + component.options[:integrative], + component.options[:differential], + component.options[:dividor] ) + +regul.pidcontroller.set_consign(component.options[:initial_value] || 0) +if (component.options[:start_at_startup]) + regul.set +end switch.on_write do |value, option| if value==1 or value==true diff -Nru openplacos-0.3+560~lucid1/debian/bzr-builder.manifest openplacos-0.4+554~ubuntu10.04.1/debian/bzr-builder.manifest --- openplacos-0.3+560~lucid1/debian/bzr-builder.manifest 2013-03-07 19:38:46.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/debian/bzr-builder.manifest 2015-01-29 17:00:45.000000000 +0000 @@ -1,3 +1,3 @@ -# bzr-builder format 0.3 deb-version 0.3+560 -lp:~openplacos/openplacos/testing revid:git-v1:d2346e65e8582589251544a2af0b00abf01a1477 -merge packaging lp:~openplacos/openplacos/debian revid:miaoufkirsh@gmail.com-20121011201721-bkrff7jmr0bw3s3v +# bzr-builder format 0.4 deb-version 0.4+554 +lp:~openplacos/openplacos/testing revid:git-v1:ca19b114917e05abd517981ec0ac73e0fb6d1dcc +merge packaging lp:~openplacos/openplacos/debian revid:miaoufkirsh@gmail.com-20140503134655-76xcs0le0c7lq21g diff -Nru openplacos-0.3+560~lucid1/debian/changelog openplacos-0.4+554~ubuntu10.04.1/debian/changelog --- openplacos-0.3+560~lucid1/debian/changelog 2013-03-07 19:38:46.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/debian/changelog 2015-01-29 17:00:45.000000000 +0000 @@ -1,8 +1,8 @@ -openplacos (0.3+560~lucid1) lucid; urgency=low +openplacos (0.4+554~ubuntu10.04.1) lucid; urgency=low * Auto build. - -- Launchpad Package Builder Thu, 07 Mar 2013 19:38:46 +0000 + -- miaoufkirsh Thu, 29 Jan 2015 17:00:45 +0000 openplacos (0.3-0ubuntu1) natty; urgency=low diff -Nru openplacos-0.3+560~lucid1/debian/control openplacos-0.4+554~ubuntu10.04.1/debian/control --- openplacos-0.3+560~lucid1/debian/control 2013-03-07 19:38:46.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/debian/control 2015-01-29 17:00:44.000000000 +0000 @@ -7,7 +7,7 @@ Homepage: http://openplacos.tuxfamily.org Package: openplacos -Version: 0.3 +Version: 0.4 Architecture: all Depends: ruby1.9.1, dbus, dbus-x11, ruby1.9.1-dev, libsqlite3-dev, git, build-essential, start-stop-daemon Description: Openplacos is an open source project for home automation, aquariophily and indoor gardens. diff -Nru openplacos-0.3+560~lucid1/Gemfile openplacos-0.4+554~ubuntu10.04.1/Gemfile --- openplacos-0.3+560~lucid1/Gemfile 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/Gemfile 2015-01-29 17:00:42.000000000 +0000 @@ -5,23 +5,24 @@ gem "choice", "~> 0.1.4" gem "file-find", "~> 0.3.5" -gem "ruby-dbus-openplacos", "~> 0.7.0" -gem "rb-pid-controller", :git => 'git://github.com/flagos/rb-pid-controller' +gem "ruby-dbus-openplacos", "~> 0.7.2.2" +gem "rb-pid-controller", :git => 'https://github.com/flagos/rb-pid-controller.git' # for webserver group :webserver do - gem 'songkick-oauth2-provider', :require => 'oauth2/provider', :git => 'git://github.com/songkick/oauth2-provider' + gem 'songkick-oauth2-provider', :require => 'oauth2/provider', :git => 'https://github.com/songkick/oauth2-provider.git' gem "activerecord" gem 'sqlite3' gem 'sinatra', "~> 1.3.2" - gem 'thin', "~> 1.3.1" + gem 'thin', "~> 1.5.1" + gem 'eventmachine-le' gem 'haml' gem 'sinatra-content-for' end #for clients group :clients do - gem "openplacos", :git => 'git://github.com/openplacos/openplacos-libclient' + gem "openplacos", :git => 'https://github.com/openplacos/openplacos-libclient.git' gem "micro-optparse", "~> 1.1.5" end @@ -33,7 +34,7 @@ group :webclient do gem "sinatra", "~> 1.3.2" gem "sinatra-contrib", "~> 1.3.1" - gem "thin", "~> 1.3.1" + gem "thin", "~> 1.5.1" gem 'haml' end diff -Nru openplacos-0.3+560~lucid1/Gemfile.lock openplacos-0.4+554~ubuntu10.04.1/Gemfile.lock --- openplacos-0.3+560~lucid1/Gemfile.lock 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/Gemfile.lock 2015-01-29 17:00:42.000000000 +0000 @@ -1,11 +1,11 @@ GIT - remote: git://github.com/flagos/rb-pid-controller + remote: https://github.com/flagos/rb-pid-controller.git revision: b911c86a1055a63fcca051f3e4a63975d49ae57f specs: rb-pid-controller (0.0.1) GIT - remote: git://github.com/openplacos/openplacos-libclient + remote: https://github.com/openplacos/openplacos-libclient.git revision: e9bcbbbd61822231f09d3c9ea5da849298a1cedb specs: openplacos (0.0.9) @@ -14,8 +14,8 @@ ruby-dbus-openplacos (~> 0.7.0) GIT - remote: git://github.com/songkick/oauth2-provider - revision: 34e65c6b75d570632cf18236d0b68154d56fc5a0 + remote: https://github.com/songkick/oauth2-provider.git + revision: d39e5735543b6ea33fff2582d4041276c62da627 specs: songkick-oauth2-provider (0.10.2) activerecord @@ -45,7 +45,8 @@ daemons (1.1.9) diff-lcs (1.2.1) eventmachine (1.0.1) - faraday (0.8.6) + eventmachine-le (1.1.5) + faraday (0.8.7) multipart-post (~> 1.1) ffi (1.4.0) file-find (0.3.5) @@ -78,7 +79,7 @@ rspec-expectations (2.13.0) diff-lcs (>= 1.1.3, < 2.0) rspec-mocks (2.13.0) - ruby-dbus-openplacos (0.7.2.1) + ruby-dbus-openplacos (0.7.2.2) serialport (1.1.0) sinatra (1.3.5) rack (~> 1.4) @@ -96,7 +97,7 @@ sqlite3 (1.3.7) sys-admin (1.6.0) ffi (>= 1.1.0) - thin (1.3.1) + thin (1.5.1) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) @@ -109,6 +110,7 @@ DEPENDENCIES activerecord choice (~> 0.1.4) + eventmachine-le file-find (~> 0.3.5) haml highline @@ -117,11 +119,11 @@ rb-pid-controller! rink rspec - ruby-dbus-openplacos (~> 0.7.0) + ruby-dbus-openplacos (~> 0.7.2.2) serialport (~> 1.1.0) sinatra (~> 1.3.2) sinatra-content-for sinatra-contrib (~> 1.3.1) songkick-oauth2-provider! sqlite3 - thin (~> 1.3.1) + thin (~> 1.5.1) diff -Nru openplacos-0.3+560~lucid1/Makefile openplacos-0.4+554~ubuntu10.04.1/Makefile --- openplacos-0.3+560~lucid1/Makefile 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/Makefile 2015-01-29 17:00:42.000000000 +0000 @@ -28,11 +28,6 @@ @cp -R setup_files $(DESTDIR)/$(INSTALLDIR) @cp -R utils $(DESTDIR)/$(INSTALLDIR) - @cp config/default.yaml $(DESTDIR)/$(DEFAULTCONFDIR)/openplacos - @cp setup_files/openplacos-server $(DESTDIR)/$(BINDIR)/openplacos-server @cp setup_files/openplacos-cli $(DESTDIR)/$(BINDIR)/openplacos - @cp setup_files/openplacos-web $(DESTDIR)/$(BINDIR)/openplacos-web - - install -m a+w -d $(DESTDIR)/$(INSTALLDIR)/clients/web-client/tmp diff -Nru openplacos-0.3+560~lucid1/server/db/008_create_readhours_tables.rb openplacos-0.4+554~ubuntu10.04.1/server/db/008_create_readhours_tables.rb --- openplacos-0.3+560~lucid1/server/db/008_create_readhours_tables.rb 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/db/008_create_readhours_tables.rb 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,13 @@ +class CreateReadhoursTables < ActiveRecord::Migration + def self.up + create_table :readhours do |t| + t.column :value, :blob + t.datetime :created_at + t.references(:interface) + end + end + + def self.down + drop_table :readhours + end +end diff -Nru openplacos-0.3+560~lucid1/server/db/009_create_readdays_tables.rb openplacos-0.4+554~ubuntu10.04.1/server/db/009_create_readdays_tables.rb --- openplacos-0.3+560~lucid1/server/db/009_create_readdays_tables.rb 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/db/009_create_readdays_tables.rb 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,13 @@ +class CreateReaddaysTables < ActiveRecord::Migration + def self.up + create_table :readdays do |t| + t.column :value, :blob + t.datetime :created_at + t.references(:interface) + end + end + + def self.down + drop_table :readdays + end +end diff -Nru openplacos-0.3+560~lucid1/server/main.rb openplacos-0.4+554~ubuntu10.04.1/server/main.rb --- openplacos-0.3+560~lucid1/server/main.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/main.rb 2015-01-29 17:00:42.000000000 +0000 @@ -36,7 +36,6 @@ require 'logger' require 'haml' - # List of local include require 'globals.rb' require 'User.rb' @@ -61,6 +60,7 @@ p.option :log, "path to logfile", :default => "/tmp/opos.log" p.option :daemon, "run server as a daemon" p.option :pid_dir, "directory for pid file. PID file will be named openplacos.pid", :default => "" + p.option :db_path, "path for sqlite db", :default => "#{SERVER_PATH}/tmp/database.db" end.process! $DEBUG = options[:debug] @@ -71,7 +71,7 @@ # create the webserver pid_dir = options[:pid_dir] -server = ThinServer.new('0.0.0.0', options[:port], pid_dir) +server = ThinServer.new('::', options[:port], pid_dir) # daemonize if requested # should be done before dbus @@ -86,12 +86,17 @@ Process.exit 1 end +#Set timezone to UTC +Time.zone = "UTC" + #Database connexion -ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "#{SERVER_PATH}/tmp/database.db", :pool => 25) +ActiveRecord::Base.default_timezone = :utc +ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => options[:db_path], :pool => 25) ActiveRecord::Base.logger = Logger.new("#{SERVER_PATH}/tmp/database.log") ActiveRecord::Migrator.migrate("#{SERVER_PATH}db") + #DBus InternalBus = DBus::ASessionBus.new @@ -153,7 +158,7 @@ if (top.debug_mode_activated) Globals.trace("At least one component is under debug, no tracker activated", Logger::WARN) else - tracker = Tracker.new(top,10) + tracker = Tracker.new(top,60) tracker.track end diff -Nru openplacos-0.3+560~lucid1/server/migrate.rb openplacos-0.4+554~ubuntu10.04.1/server/migrate.rb --- openplacos-0.3+560~lucid1/server/migrate.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/migrate.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -#!/usr/bin/env ruby - -require "rubygems" -require "bundler/setup" -require "active_record" -require "oauth2/provider" - -ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => "test.db", :pool => 25) -ActiveRecord::Migrator.migrate('db') diff -Nru openplacos-0.3+560~lucid1/server/Models.rb openplacos-0.4+554~ubuntu10.04.1/server/Models.rb --- openplacos-0.3+560~lucid1/server/Models.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/Models.rb 2015-01-29 17:00:42.000000000 +0000 @@ -9,12 +9,26 @@ belongs_to :resource has_many :reads + has_many :readhours + has_many :readdays end class Read < ActiveRecord::Base belongs_to :interface +end + +class Readhour < ActiveRecord::Base + + belongs_to :interface + +end + +class Readday < ActiveRecord::Base + + belongs_to :interface + end class Introspect < ActiveRecord::Base diff -Nru openplacos-0.3+560~lucid1/server/public/javascript/highstock.js openplacos-0.4+554~ubuntu10.04.1/server/public/javascript/highstock.js --- openplacos-0.3+560~lucid1/server/public/javascript/highstock.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/javascript/highstock.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,27 +1,334 @@ -$(function() { - $.getJSON('http://localhost:4567/ressources/home/temperature?iface=analog.sensor.temperature.celcuis&start_time=', function(data) { - // Create the chart - window.chart = new Highcharts.StockChart({ - chart: { - renderTo: 'container' - }, +/* + Highstock JS v1.3.2 (2013-06-05) - rangeSelector: { - selected: 1 - }, + (c) 2009-2013 Torstein Hønsi - title: { - text: 'AAPL Stock Price' - }, - - series: [{ - name: 'AAPL Stock Price', - data: data, - type: 'spline', - tooltip: { - valueDecimals: 2 - } - }] - }); - }); -}); + License: www.highcharts.com/license +*/ +(function(){function y(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function z(){var a,b=arguments.length,c={},d=function(a,b){var c,h;for(h in b)b.hasOwnProperty(h)&&(c=b[h],typeof a!=="object"&&(a={}),a[h]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?d(a[h]||{},c):b[h]);return a};for(a=0;a3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+U(a-c).toFixed(f).slice(2):"")}function Ka(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function sa(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments); +a.unshift(d);return c.apply(this,a)}}function La(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h-1?h.thousandsSep:"")):e=ya(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function yb(a,b,c,d){var e,c=p(c,1);e=a/c;b||(b=[1, +2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=I[eb]&&(i.setMilliseconds(0),i.setSeconds(j>=I[Ya]?0:k*W(i.getSeconds()/k)));if(j>=I[Ya])i[Nb](j>=I[za]?0:k*W(i[Ab]()/k));if(j>=I[za])i[Ob](j>=I[ga]?0:k*W(i[Bb]()/k));if(j>=I[ga])i[Cb](j>=I[Na]?1:k*W(i[Oa]()/k));j>=I[Na]&&(i[Pb](j>=I[ta]?0:k*W(i[lb]()/k)),h=i[mb]());j>=I[ta]&&(h-=h%k,i[Qb](h));if(j===I[Ma])i[Cb](i[Oa]()- +i[Db]()+p(d,1));b=1;h=i[mb]();for(var d=i.getTime(),m=i[lb](),l=i[Oa](),o=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;dc&&(c=a[b]);return c}function Aa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Za(a){ob||(ob=aa(Qa));a&&ob.appendChild(a);ob.innerHTML=""}function Ba(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else Y.console&&console.log(c)}function oa(a){return parseFloat(a.toPrecision(14))} +function $a(a,b){Ra=p(a,b.animation)}function Tb(){var a=M.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";nb=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,p(c,1),p(g,0),p(h,0),p(i,0))).getTime()};Ab=b+"Minutes";Bb=b+"Hours";Db=b+"Day";Oa=b+"Date";lb=b+"Month";mb=b+"FullYear";Nb=c+"Minutes";Ob=c+"Hours";Cb=c+"Date";Pb=c+"Month";Qb=c+"FullYear"}function Ca(){}function ab(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function Fb(a,b){this.axis=a;if(b)this.options= +b,this.id=b.id}function Ub(a,b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:p(b.y,g?4:c?14:-6),x:p(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function Da(){this.init.apply(this,arguments)}function Gb(){this.init.apply(this,arguments)}function pb(a,b){this.init(a, +b)}function Hb(a,b){this.init(a,b)}function Sa(){this.init.apply(this,arguments)}function Ib(a){var b=a.options,c=b.navigator,d=c.enabled,b=b.scrollbar,e=b.enabled,f=d?c.height:0,g=e?b.height:0,h=c.baseSeries;this.baseSeries=a.series[h]||typeof h==="string"&&a.get(h)||a.series[0];this.handles=[];this.scrollbarButtons=[];this.elementsToDestroy=[];this.chart=a;this.height=f;this.scrollbarHeight=g;this.scrollbarEnabled=e;this.navigatorEnabled=d;this.navigatorOptions=c;this.scrollbarOptions=b;this.outlineHeight= +f+g;this.init()}function Jb(a){this.init(a)}var w,H=document,Y=window,P=Math,r=P.round,W=P.floor,pa=P.ceil,t=P.max,A=P.min,U=P.abs,ha=P.cos,ka=P.sin,bb=P.PI,qb=bb*2/360,Ta=navigator.userAgent,Vb=Y.opera,Xa=/msie/i.test(Ta)&&!Vb,rb=H.documentMode===8,sb=/AppleWebKit/.test(Ta),tb=/Firefox/.test(Ta),ub=/(Mobile|Android|Windows Phone)/.test(Ta),Ea="http://www.w3.org/2000/svg",ca=!!H.createElementNS&&!!H.createElementNS(Ea,"svg").createSVGRect,cc=tb&&parseInt(Ta.split("Firefox/")[1],10)<4,ia=!ca&&!Xa&& +!!H.createElement("canvas").getContext,cb,gb=H.documentElement.ontouchstart!==w,Wb={},Kb=0,ob,M,ya,Ra,Lb,I,qa=function(){},Ua=[],Qa="div",ba="none",Xb="rgba(192,192,192,"+(ca?1.0E-4:0.002)+")",kb="millisecond",eb="second",Ya="minute",za="hour",ga="day",Ma="week",Na="month",ta="year",Yb="stroke-width",nb,Ab,Bb,Db,Oa,lb,mb,Nb,Ob,Cb,Pb,Qb,Q={};Y.Highcharts=Y.Highcharts?Ba(16,!0):{};ya=function(a,b,c){if(!v(b)||isNaN(b))return"Invalid date";var a=p(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[Bb](),g=d[Db](), +h=d[Oa](),i=d[lb](),j=d[mb](),k=M.lang,m=k.weekdays,d=y({a:m[g].substr(0,3),A:m[g],d:Ka(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ka(i+1),y:j.toString().substr(2,2),Y:j,H:Ka(f),I:Ka(f%12||12),l:f%12||12,M:Ka(d[Ab]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:Ka(d.getSeconds()),L:Ka(r(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Rb.prototype={wrapColor:function(a){if(this.color>= +a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};I=jb(kb,1,eb,1E3,Ya,6E4,za,36E5,ga,864E5,Ma,6048E5,Na,26784E5,ta,31556952E3);Lb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift= +0;if(b.length)for(a=c.length;b.length{point.key}
',pointFormat:'{series.name}: {point.y}
',shadow:!0,snap:ub?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer", +color:"#909090",fontSize:"9px"}}};var S=M.plotOptions,R=S.line;Tb();var wa=function(a){var b=[],c,d;(function(a){a&&a.stops?d=Fa(a.stops,function(a){return wa(a[1])}):(c=/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(a))?b=[C(c[1]),C(c[2]),C(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))?b=[C(c[1],16),C(c[2],16),C(c[3],16),1]:(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))&& +(b=[C(c[1]),C(c[2]),C(c[3]),1])})(a);return{get:function(c){var f;d?(f=z(a),f.stops=[].concat(f.stops),n(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)n(d,function(b){b.brighten(a)});else if(Ja(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=C(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};Ca.prototype= +{init:function(a,b){this.element=b==="span"?aa(b):H.createElementNS(Ea,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=p(b,Ra,!0);hb(this);if(b){b=z(b);if(c)b.complete=c;Mb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,m=this.shadows,l,o,q=this;ma(a)&&v(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),q=G(g,c)|| +this[c]||0,c!=="d"&&c!=="visibility"&&(q=parseFloat(q));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==w&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;el&&/[ \-]/.test(b.textContent||b.innerText))L(b,{width:l+"px",display:"block",whiteSpace:"normal"}),k=l;l=a.fontMetrics(b.style.fontSize).b;u=q<0&&-k;s=o<0&&-m;O=q*o<0;u+=o*l*(O?1-h:h);s-=q*l*(j? +O?h:1-h:1);i&&(u-=k*h*(q<0?-1:1),j&&(s-=m*h*(o<0?-1:1)),L(b,{textAlign:g}));this.xCorr=u;this.yCorr=s}L(b,{left:e+u+"px",top:f+s+"px"});if(sb)m=b.offsetHeight;this.cTT=x}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")"); +(v(c)||v(d))&&a.push("scale("+p(c,1)+" "+p(d,1)+")");a.length&&G(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||ma(c))this.alignTo=d=c||"renderer",na(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=p(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x|| +0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=r(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=r(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d=this.rotation;c=this.element;var e=this.styles,f=d*qb;if(!a){if(c.namespaceURI===Ea||b.forExport){try{a=c.getBBox?y({},c.getBBox()): +{width:c.offsetWidth,height:c.offsetHeight}}catch(g){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){b=a.width;c=a.height;if(Xa&&e&&e.fontSize==="11px"&&c.toPrecision(3)==="22.7")a.height=c=14;if(d)a.width=U(c*ka(f))+U(b*ha(f)),a.height=U(c*ha(f))+U(b*ka(f))}this.bBox=a}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})}, +add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=G(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=C(g);if(c.handleZ)for(c=0;cg||!v(g)&&v(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;K(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a= +this,b=a.element||{},c=a.shadows,d,e;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;hb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(e=0;e/g,'').replace(/<(i|em)>/g,'').replace(//g,"").split(//g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="([^"]+)"/,i=G(b,"x"),j=a.styles,k=j&&j.width&&C(j.width),m=j&&j.lineHeight,l=f.length;l--;)b.removeChild(f[l]);k&&!a.added&&this.box.appendChild(b); +e[e.length-1]===""&&e.pop();n(e,function(e,f){var l,u=0,e=e.replace(//g,"|||");l=e.split("|||");n(l,function(e){if(e!==""||l.length===1){var o={},n=H.createElementNS(Ea,"tspan"),p;g.test(e)&&(p=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),G(n,"style",p));h.test(e)&&!d&&(G(n,"onclick",'location.href="'+e.match(h)[1]+'"'),L(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");n.appendChild(H.createTextNode(e)); +u?o.dx=0:o.x=i;G(n,o);!u&&f&&(!ca&&d&&L(n,{display:"block"}),G(n,"dy",m||c.fontMetrics(/px$/.test(n.style.fontSize)?n.style.fontSize:j.fontSize).h,sb&&n.offsetHeight));b.appendChild(n);u++;if(k)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),E,B=[];e.length||B.length;)delete a.bBox,E=a.getBBox().width,o=E>k,!o||e.length===1?(e=B,B=[],e.length&&(n=H.createElementNS(Ea,"tspan"),G(n,{dy:m||16,x:i}),p&&G(n,"style",p),b.appendChild(n),E>k&&(k=E))):(n.removeChild(n.firstChild),B.unshift(e.pop())),e.length&& +n.appendChild(H.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c,null,null,null,null,null,"button"),i=0,j,k,m,l,o,a={x1:0,y1:0,x2:0,y2:1},e=z({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);m=e.style;delete e.style;f=z(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);l=f.style;delete f.style;g=z(e,{stroke:"#68A",fill:{linearGradient:a, +stops:[[0,"#9BD"],[1,"#CDF"]]}},g);o=g.style;delete g.style;F(h.element,"mouseenter",function(){h.attr(f).css(l)});F(h.element,"mouseleave",function(){j=[e,f,g][i];k=[m,l,o][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(o):h.attr(e).css(m)};return h.on("click",function(){d.call(h)}).attr(e).css(y({cursor:"default"},m))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=r(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=r(a[2])+b%2/2);return a},path:function(a){var b={fill:ba};Wa(a)?b.d= +a:da(a)&&y(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=da(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(da(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=da(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:ba});return e.attr(da(a)?a:e.crisp(f,a,b,t(c,0),t(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects, +e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return v(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:ba};arguments.length>1&&y(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f}, +symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(r(b),r(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),y(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&y(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(r((d-b[0])/2),r((e-b[1])/2)))},j=a.match(i)[1],a=Wb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),aa("img",{onload:function(){k(g,Wb[j]=[this.width,this.height])},src:j}));return g}, +symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR, +h=e.open,i=ha(f),j=ka(f),k=ha(g),g=ka(g),e=e.end-f');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=aa(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer, +c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();K(this,"add");return this},updateTransform:Ca.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,m=this.shadows,l,o=this.attrSetters,q=this;ma(a)&&v(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,q=c==="strokeWidth"||c==="stroke-width"?this.strokeweight: +this[c];else for(c in a)if(d=a[c],l=!1,e=o[c]&&o[c].call(this,d,c),e!==!1&&d!==null){e!==w&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),l=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;l=[];for(var n;e--;)if(Ja(d[e]))l[e]=r(d[e]*10)-5;else if(d[e]==="Z")l[e]="x";else if(l[e]=d[e],d.isArc&&(d[e]==="wa"||d[e]==="at"))n=d[e]==="wa"?1:-1,l[e+5]===l[e+7]&&(l[e+7]-=n),l[e+6]===l[e+8]&&(l[e+8]-=n);d=l.join(" ")||"x";f.path=d;if(m)for(e= +m.length;e--;)m[e].path=m[e].cutOff?this.cutOffPath(d,m[e].cutOff):d;l=!0}else if(c==="visibility"){if(m)for(e=m.length;e--;)m[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,rb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;l=!0}else if(c==="zIndex")d&&(g[c]=d),l=!0;else if(va(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=t(0,d),this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,l=!0;else if(c==="class"&&h==="DIV")f.className=d;else if(c==="stroke")d= +i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ja(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||aa(i.prepVML([""]),null,null,f))[c]=d||"solid",this.dashstyle=d,l=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==ba?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c==="opacity")l=!0;else if(h==="shape"&&c==="rotation")this[c]=d,f.style.left=-r(ka(d*qb)+1)+"px", +f.style.top=r(ha(d*qb))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),l=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,l=!0;l||(rb?f[c]=d:G(f,c,d))}return q},clip:function(a){var b=this,c;a?(c=a.members,na(c,b),c.push(b),b.destroyClip=function(){na(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:rb?"inherit":"rect(auto)"});return b.css(a)},css:Ca.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Za(a)},destroy:function(){this.destroyClip&& +this.destroyClip();return Ca.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=Y.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=C(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,m,l,o,q;k&&typeof k.value!=="string"&&(k="x");l=k;if(a){o=p(a.width,3);q=(a.opacity||0.15)/o;for(e=1;e<=3;e++){m=o*2+1-2*e; +c&&(l=this.cutOffPath(k.value,m+0.5));j=[''];h=aa(g.prepVML(j),null,{left:C(i.left)+p(a.offsetX,1),top:C(i.top)+p(a.offsetY,1)});if(c)h.cutOff=m+1;j=[''];aa(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}},Z=ea(Ca,Z),Z={Element:Z,isIE8:Ta.indexOf("MSIE 8.0")> +-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(Qa);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!H.namespaces.hcv)H.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),H.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e= +this.createElement(),f=da(a);return y(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+r(a?e:d)+"px,"+r(a?f:b)+"px,"+r(a?b:f)+"px,"+r(a?d:e)+"px)"};!a&&rb&&c==="DIV"&&y(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e= +this,f,g=/^rgba/,h,i,j=ba;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,m,l=a.linearGradient||a.radialGradient,o,q,p,u,s,x="",a=a.stops,N,fa=[],E=function(){h=[''];aa(e.prepVML(h),null,null,b)};o=a[0];N=a[a.length-1];o[0]>0&&a.unshift([0,o[1]]);N[0]<1&&a.push([1,N[1]]);n(a,function(a,b){g.test(a[1])?(f=wa(a[1]),k=f.get("rgb"),m=f.get("a")):(k=a[1],m=1); +fa.push(a[0]*100+"% "+k);b?(p=m,u=k):(q=m,s=k)});if(c==="fill")if(i==="gradient")c=l.x1||l[0]||0,a=l.y1||l[1]||0,o=l.x2||l[2]||0,l=l.y2||l[3]||0,x='angle="'+(90-P.atan((l-a)/(o-c))*180/bb)+'"',E();else{var j=l.r,B=j*2,T=j*2,t=l.cx,w=l.cy,v=b.radialReference,r,j=function(){v&&(r=d.getBBox(),t+=(v[0]-r.x)/r.width-0.5,w+=(v[1]-r.y)/r.height-0.5,B*=v[2]/r.width,T*=v[2]/r.height);x='src="'+M.global.VMLRadialGradientURL+'" size="'+B+","+T+'" origin="0.5,0.5" position="'+t+","+w+'" color2="'+s+'" ';E()}; +d.added?j():F(d,"add",j);j=u}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=wa(a),h=["<",c,' opacity="',f.get("a"),'"/>'],aa(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="', +'style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(da(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,t(c,0),t(d,0)))},invertChild:function(a,b){var c=b.style;L(a,{flip:"x",left:C(c.width)-1,top:C(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=ha(f),i=ka(f),j=ha(g),k=ka(g);if(g- +f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!v(e)||!e.r?f=Ga.prototype.symbols.square.apply(0,arguments):(h=A(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*h,f,g,f,g-h,f- +h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}},Highcharts.VMLRenderer=ib=function(){this.init.apply(this,arguments)},ib.prototype=z(Ga.prototype,Z),cb=ib;var $b;if(ia)Highcharts.CanVGRenderer=Z=function(){Ea="http://www.w3.org/1999/xhtml"},Z.prototype.symbols={},$b=function(){function a(){var a=b.length,d;for(d=0;dj&&(c=!1)):h+k>l&&(h=l-k,d&&h+m0&&b.height>0){f=z({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=N.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:s}).css(f.style).add();b=[q[1],q[4],p(q[6],q[1])];q=[q[2],q[5],p(q[7],q[2])];c=Pa(b);k=Pa(q);g.align(f,!1,{x:c,y:k,width:ua(b)-c,height:ua(q)-k});g.show()}else g&&g.hide(); +return a},destroy:function(){na(this.axis.plotLinesAndBands,this);Aa(this,this.axis)}};Ub.prototype={destroy:function(){Aa(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options,c=b.format,c=c?La(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c= +this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=U(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?ca?"inherit":"visible":"hidden"})}};Da.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S", +minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:D,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e", +fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return xa(this.total,-1)},style:D.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}}, +defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions= +b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=v(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0; +this.stacks={};this._stacksTouched=0;this.min=this.max=null;var f,d=this.options.events;va(this,a.axes)===-1&&(a.axes.push(this),a[c?"xAxis":"yAxis"].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===w)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)F(this,f,d[f]);if(this.isLog)this.val2lin=ra,this.lin2val=la},setOptions:function(a){this.options=z(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions, +this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],z(M[this.isXAxis?"xAxis":"yAxis"],a))},update:function(a,b){var c=this.chart,a=c.options[this.xOrY+"Axis"][this.options.index]=z(this.userOptions,a);this.destroy();this._addedPlotLB=!1;this.init(c,a);c.isDirtyBox=!0;p(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.xOrY+"Axis";n(this.series,function(a){a.remove(!1)});na(b.axes,this);na(b[c],this);b.options[c].splice(this.options.index, +1);n(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;p(a,!0)&&b.redraw()},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=M.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=La(h,this);else if(c)g=b;else if(d)g=ya(d,b);else if(f&&a>=1E3)for(;f--&&g===w;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=xa(b/c,-1)+e[f]);g===w&&(g=b>=1E3?xa(b,0):xa(b,-1));return g},getSeriesExtremes:function(){var a= +this,b=a.chart,c=a.stacks,d=[],e=[],f=a._stacksTouched+=1,g,h;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var j=g.options,k,m,l,o,q,n,u,s,x,N=j.threshold,fa,E=[],B=0;a.hasVisibleSeries=!0;if(a.isLog&&N<=0)N=j.threshold=null;if(a.isXAxis){if(j=g.xData,j.length)a.dataMin=A(p(a.dataMin,j[0]),Pa(j)),a.dataMax=t(p(a.dataMax,j[0]),ua(j))}else{var T,r,J,z=g.cropped,y=g.xAxis.getExtremes(),C=!!g.modifyValue;k=j.stacking;a.usePercentage= +k==="percent";if(k)q=j.stack,o=g.type+p(q,""),n="-"+o,g.stackKey=o,m=d[o]||[],d[o]=m,l=e[n]||[],e[n]=l;if(a.usePercentage)a.dataMin=0,a.dataMax=99;j=g.processedXData;u=g.processedYData;fa=u.length;for(h=0;h0))if(C&&(x=g.modifyValue(x)),g.getExtremesFromAll|| +z||(j[h+1]||s)>=y.min&&(j[h-1]||s)<=y.max)if(s=x.length)for(;s--;)x[s]!==null&&(E[B++]=x[s]);else E[B++]=x}if(!a.usePercentage&&E.length)g.dataMin=k=Pa(E),g.dataMax=g=ua(E),a.dataMin=A(p(a.dataMin,k),k),a.dataMax=t(p(a.dataMax,g),g);if(v(N))if(a.dataMin>=N)a.dataMin=N,a.ignoreMinPadding=!0;else if(a.dataMaxf+this.width)l=!0}else if(c=f,i=m-this.right,hg+this.height)l=!0;return l&&!d?null:e.renderer.crispLine(["M",c,h,"L",i,j],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4], +c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=oa(W(b/a)*a),c=oa(pa(c/a)*a),e=[];b<=c;){e.push(b);b=oa(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=r(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=W(b),h,i,j,k,m,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&(!d|| +k<=c)&&g.push(k),k>c&&(m=!0),k=j}else if(b=la(b),c=la(c),a=e[d?"minorTickInterval":"tickInterval"],a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=yb(a,null,P.pow(10,W(P.log(a)/P.LN10))),g=Fa(this.getLinearTickPositions(a,b,c),ra),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length; +for(a=1;a=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===w&&!this.isLog)v(a.min)||v(a.max)?this.minRange=null:(n(this.series, +function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===w||hb&&(g=0);c=t(c,g);e=t(e,h?0:g/2);f=t(f,h==="on"?0:g);!a.noSharedTooltip&&v(m)&&(d=v(d)?A(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=A(c,b),this.closestPointRange=d;if(a)this.oldTransA=h;this.translationSlope=this.transA=h=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding= +h*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,m=d.tickInterval,l=d.minTickInterval,o=d.tickPixelInterval,q=b.categories;h?(b.linkedParent=c[g?"xAxis":"yAxis"][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=p(c.min,c.dataMin),b.max=p(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&Ba(11,1)):(b.min=p(b.userMin,d.min,b.dataMin),b.max=p(b.userMax,d.max,b.dataMax)); +if(e)!a&&A(b.min,p(b.dataMin,b.min))<=0&&Ba(10,1),b.min=oa(ra(b.min)),b.max=oa(ra(b.max));if(b.range&&(b.userMin=b.min=t(b.min,b.max-b.range),b.userMax=b.max,a))b.range=null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!q&&!b.usePercentage&&!h&&v(b.min)&&v(b.max)&&(c=b.max-b.min)){if(!v(d.min)&&!v(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!v(d.max)&&!v(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max=== +void 0?1:h&&!m&&o===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:p(m,q?1:(b.max-b.min)*o/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!m&&b.tickIntervale&&i.shift(),d.endOnTick?b.max=f:b.max+h(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&& +!this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e=this.dataMax&&(b=w));this.displayBtn=a!==w||b!==w;this.setExtremes(a,b,!1,w,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=p(b.left,a.plotLeft+c);this.top=f=p(b.top,a.plotTop);this.width=c=p(b.width,a.plotWidth-c+d);this.height=b=p(b.height,a.plotHeight); +this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=t(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?oa(la(this.min)):this.min,max:a?oa(la(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?la(this.min):this.min,b=b?la(this.max):this.max;c>a||a===null?a=c:b=a.min&&b<=a.max)j[b]||(j[b]=new ab(a,b)),s&&j[b].isNew&&j[b].render(c,!0),j[b].render(c,!1,1)}),q&&a.min===0&&(j[-1]||(j[-1]=new ab(a,-1,null,!0)),j[-1].render(-1))), +o&&n(g,function(b,c){if(c%2===0&&b1||U(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},p(this.options.hideDelay,500)),b&&n(b,function(a){a.setState()}),this.chart.hoverPoints=null},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e= +d.inverted,f=d.plotTop,g=0,h=0,i,a=ja(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===w&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(n(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Fa(c,r)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i= +p(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,m;d<7&&(d=e+t(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,m=!0);k=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=t(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ja(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];n(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat|| +"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h,i={},j,k=[];j=e.formatter||this.defaultFormatter;var i=c.hoverPoints,m,l=e.crosshairs;h=this.shared;clearTimeout(this.hideTimer);this.followPointer=ja(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];h&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,i&&n(i,function(a){a.setState()}),n(a,function(a){a.setState("hover");k.push(a.getLabelConfig())}),i={x:a[0].category, +y:a[0].y},i.points=k,a=a[0]):i=a.getLabelConfig();j=j.call(i,this);i=a.series;h=h||!i.isCartesian||i.tooltipOutsidePlot||c.isInsidePlot(f,g);j===!1||!h?this.hide():(this.isHidden&&(hb(d),d.attr("opacity",1).show()),d.attr({text:j}),m=e.borderColor||a.color||i.color||"#606060",d.attr({stroke:m}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);if(l){l=ja(l);for(d=l.length;d--;)if(e=a.series[d?"yAxis":"xAxis"],l[d]&&e)if(h=d?p(a.stackY,a.y):a.x,e.isLog&&(h=ra(h)),e=e.getPlotLinePath(h,1),this.crosshairs[d])this.crosshairs[d].attr({d:e, +visibility:"visible"});else{h={"stroke-width":l[d].width||1,stroke:l[d].color||"#C0C0C0",zIndex:l[d].zIndex||2};if(l[d].dashStyle)h.dashstyle=l[d].dashStyle;this.crosshairs[d]=c.renderer.path(e).attr(h).add()}}K(c,"tooltipRefresh",{text:j,x:f+c.plotLeft,y:g+c.plotTop,borderColor:m})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(r(c.x),r(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};pb.prototype={init:function(a, +b){var c=ia?"":b.chart.zoomType,d=a.inverted,e;this.options=b;this.chart=a;this.zoomX=e=/x/.test(c);this.zoomY=c=/y/.test(c);this.zoomHor=e&&!d||c&&d;this.zoomVert=c&&!d||e&&d;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new Gb(a,b.tooltip);this.setDOMEvents()},normalize:function(a){var b,c,d,a=a||Y.event;if(!a.target)a.target=a.srcElement;a=Zb(a);d=a.touches?a.touches.item(0):a;this.chartPosition=b=ec(this.chart.container);d.pageX===w?(c=a.x,b=a.y):(c=d.pageX-b.left,b= +d.pageY-b.top);return y(a,{chartX:r(c),chartY:r(b)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};n(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&& +(!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;ij&&e.splice(h,1);if(e.length&&e[0].clientX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].clientX}if(g&&g.tracker){if((b=g.tooltipPoints[k])&&b!==f)b.onMouseOver(a)}else d&&d.followPointer&&!d.isHidden&&(a=d.getAnchor([{}],a), +d.updatePosition({plotX:a[0],plotY:a[1]}))},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&ja(b)[0].plotX===w&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart;n(c.series,function(d){d.xAxis&&d.xAxis.zoomEnabled&&(d.group.attr(a),d.markerGroup&&(d.markerGroup.attr(a),d.markerGroup.clip(b?c.clipRect:null)),d.dataLabelsGroup&& +d.dataLabelsGroup.attr(a))});c.clipRect.attr(b||c.clipBox)},pinchTranslateDirection:function(a,b,c,d,e,f,g){var h=this.chart,i=a?"x":"y",j=a?"X":"Y",k="chart"+j,m=a?"width":"height",l=h["plot"+(a?"Left":"Top")],o,q,n=1,p=h.inverted,s=h.bounds[a?"h":"v"],x=b.length===1,r=b[0][k],t=c[0][k],E=!x&&b[1][k],B=!x&&c[1][k],v,c=function(){!x&&U(r-E)>20&&(n=U(t-B)/U(r-E));q=(l-t)/n+r;o=h["plot"+(a?"Width":"Height")]/n};c();b=q;bs.max&&(b=s.max-o,v=!0);v?(t-=0.8*(t-g[i][0]),x||(B-= +0.8*(B-g[i][1])),c()):g[i]=[t,B];p||(f[i]=q-l,f[m]=o);f=p?1/n:n;e[m]=o;e[i]=b;d[p?a?"scaleY":"scaleX":"scale"+j]=n;d["translate"+j]=f*l+(t-f*r)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,m=b.selectionMarker,l={},o={};a.type==="touchstart"&&(e||k)&&a.preventDefault();Fa(f,function(a){return b.normalize(a)});if(a.type==="touchstart")n(f,function(a, +b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],n(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=A(e,f),e=t(e,f);b.min=A(a.pos,g-d);b.max=t(a.pos+a.len,e+d)}});else if(d.length){if(!m)b.selectionMarker=m=y({destroy:qa},c.plotBox);i&&b.pinchTranslateDirection(!0,d,f,l,m,o,h);j&&b.pinchTranslateDirection(!1,d,f,l,m,o,h);b.hasPinched=k;b.scaleGroups(l, +o);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,a=a.chartY,e=this.zoomHor,f=this.zoomVert,g=b.plotLeft,h=b.plotTop,i=b.plotWidth,j=b.plotHeight,k,m=this.mouseDownX,l=this.mouseDownY;dg+i&&(d=g+i);ah+j&&(a=h+j);this.hasDragged=Math.sqrt(Math.pow(m-d,2)+Math.pow(l-a,2));if(this.hasDragged> +10){k=b.isInsidePlot(m-g,l-h);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&k&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(g,h,e?1:i,f?1:j,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&e&&(e=d-m,this.selectionMarker.attr({width:U(e),x:(e>0?0:e)+m}));this.selectionMarker&&f&&(e=a-l,this.selectionMarker.attr({height:U(e),y:(e>0?0:e)+l}));k&&!this.selectionMarker&&c.panning&&b.pan(d)}},drop:function(a){var b=this.chart,c=this.hasPinched; +if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)n(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.xOrY+"Axis"].push({axis:a,min:A(c,b),max:t(c,b)}),h=!0)}}),h&&K(b,"selection",d,function(a){b.zoom(y(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups({translateX:b.plotLeft, +translateY:b.plotTop,scaleX:1,scaleY:1})}if(b)L(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=Zb(a);c&&d&&d.isCartesian&&!b.isInsidePlot(a.pageX-c.left-b.plotLeft,a.pageY-c.top- +b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);a.returnValue=!1;b.mouseIsDown==="mousedown"&&this.drag(a);b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=G(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b= +this.chart.hoverSeries;if(b&&!b.options.stickyTracking&&!this.inClass(a.toElement||a.relatedTarget,"highcharts-tooltip"))b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,y(c,{pageX:g.left+d+(f?b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),K(c.series,"click",y(a,{point:c})),b.hoverPoint&& +c.firePointEvent("click",a)):(y(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&K(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&(this.runPointActions(a),this.pinch(a))):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a= +this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[H,"mousemove","onDocumentMouseMove"],[H,"mouseup","onDocumentMouseUp"]];gb&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[H,"touchend","onDocumentTouchEnd"]);n(c,function(b){a["_"+b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]: +F(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;n(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:X(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};Hb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=p(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=C(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=z(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight= +0,c.lastLineHeight=0,c.render(),F(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g)for(j in g=a.convertAttribs(g),g)d=g[j],d!==w&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl, +d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Za(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY, +n(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,L(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g(l||c.chartWidth-2*k-n))b.itemX=n,b.itemY+=q+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=t(b.maxItemWidth,e);b.lastItemY=q+b.itemY+o;b.lastLineHeight=t(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=q+g+o,b.lastLineHeight= +g);b.offsetWidth=l||t(f?b.itemX-n:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,m=j.borderWidth,l=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&!v(b.linkedTo)&&(e=e.concat(a.legendItems|| +(b.legendType==="point"?a.data:a)))});Sb(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(m||l){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,m||0).attr({stroke:j.borderColor, +"stroke-width":m||0,fill:l||ba}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(y({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=p(i.animation,!0),k=i.arrowSize||12,m=this.nav;e.layout=== +"horizontal"&&(f/=2);g&&(f=A(f,g));if(a>f&&!e.useHTML){this.clipHeight=c=f-20-this.titleHeight;this.pageCount=pa(a/c);this.currentPage=p(this.currentPage,1);this.fullHeight=a;if(!h)h=b.clipRect=d.clipRect(0,0,9999,0),b.contentGroup.clip(h);h.attr({height:c});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(m),this.pager=d.text("",15,10).css(i.style).add(m),this.down=d.symbol("triangle-down",0,0,k,k).on("click", +function(){b.scroll(1,j)}).add(m);b.scroll(0);a=f}else if(m)h.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,h=f.inactiveColor,f=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==w&&$a(b,this.chart),this.nav.attr({translateX:i,translateY:e+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:d===1?h:g}).css({cursor:d=== +1?"default":"pointer"}),f.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?h:g}).css({cursor:d===c?"default":"pointer"}),e=-A(e*(d-1),this.fullHeight-e+i)+1,this.scrollGroup.animate({translateY:e}),f.attr({text:d+"/"+c}),this.currentPage=d,this.positionCheckboxes(e)}};Sa.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=z(M,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=da(e)?e:[e,e,e,e];this.optionsMarginTop=p(d.marginTop,e[0]);this.optionsMarginRight= +p(d.marginRight,e[1]);this.optionsMarginBottom=p(d.marginBottom,e[2]);this.optionsMarginLeft=p(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ua.length;Ua.push(f);d.reflow!==!1&&F(f,"load",function(){f.initReflow()});if(e)for(g in e)F(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=ia?!1:p(d.animation,!0);f.pointCount=0;f.counters= +new Rb;f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=Q[a.type||b.type||b.defaultSeriesType])||Ba(17,!0);b=new b;b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(b=p(b,!0),K(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var b=b?"xAxis":"yAxis",e=this.options;new Da(this,z(a,{index:this[b].length}));e[b]=ja(e[b]||{});e[b].push(a);p(c,!0)&&this.redraw(d)},isInsidePlot:function(a, +b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,m=k.isHidden(),l=[];$a(a,this);for(m&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a= +c[j],a.options.stacking)a.isDirty=!0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,l.push(function(){K(a,"afterSetExtremes",a.getExtremes())});if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&& +a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);k.draw();K(this,"redraw");m&&this.cloneRenderTo(!0);n(l,function(a){a.call()})},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=aa(Qa,{className:"highcharts-loading"},y(d.style,{zIndex:10,display:ba}),this.container),this.loadingSpan=aa("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)L(c,{opacity:0,display:"",left:this.plotLeft+ +"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),Mb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&Mb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){L(b,{display:ba})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;dA(e.dataMin,e.min)&&c19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Za(b),delete this.renderToClone): +(c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),L(b,{position:"absolute",top:"-9999px",display:"block"}),H.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+Kb++;if(ma(a))this.renderTo=a=H.getElementById(a);a||Ba(13,!0);c=C(G(a,"data-highcharts-chart"));!isNaN(c)&&Ua[c]&&Ua[c].destroy();G(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo(); +this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=aa(Qa,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},y({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new Ga(a,c,d,!0):new cb(a,c,d);ia&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart, +b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.options.title,m=this.options.subtitle,l=this.options.legend,o=p(l.margin,10),q=l.x,O=l.y,u=l.align,s=l.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!v(this.optionsMarginTop))if(m=t(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!m.floating&&!m.verticalAlign&& +m.y||0))this.plotTop=t(this.plotTop,m+p(k.margin,15)+b);if(f.display&&!l.floating)if(u==="right"){if(!v(i))this.marginRight=t(this.marginRight,f.legendWidth-q+o+c)}else if(u==="left"){if(!v(h))this.plotLeft=t(this.plotLeft,f.legendWidth+q+o+a)}else if(s==="top"){if(!v(g))this.plotTop=t(this.plotTop,f.legendHeight+O+o+b)}else if(s==="bottom"&&!v(j))this.marginBottom=t(this.marginBottom,f.legendHeight-O+o+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+= +this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});v(h)||(this.plotLeft+=e[3]);v(g)||(this.plotTop+=e[0]);v(j)||(this.marginBottom+=e[2]);v(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||vb(d,"width"),h=c.height||vb(d,"height"),a=a?a.target:Y;if(!b.hasUserSize&&g&&h&&(a===Y||a===H)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),b.reflowTimeout=e=setTimeout(function(){if(b.container)b.setSize(g, +h,!1),b.hasUserSize=null},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;F(Y,"resize",a);F(b,"destroy",function(){X(Y,"resize",a)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&K(d,"endResize",null,function(){d.isResizing-=1})};$a(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(v(a))d.chartWidth=e=t(0,r(a)),d.hasUserSize=!!e;if(v(b))d.chartHeight=f=t(0,r(b));L(d.container,{width:e+"px",height:f+"px"});d.setChartSize(!0); +d.renderer.setSize(e,f,c);d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;K(d,"resize");Ra===!1?g():setTimeout(g,Ra&&Ra.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=f.spacingTop,h=f.spacingRight,i=f.spacingBottom,j=f.spacingLeft,k=this.clipOffset,m,l,o,q;this.plotLeft=m= +r(this.plotLeft);this.plotTop=l=r(this.plotTop);this.plotWidth=o=t(0,r(d-m-this.marginRight));this.plotHeight=q=t(0,r(e-l-this.marginBottom));this.plotSizeX=b?q:o;this.plotSizeY=b?o:q;this.plotBorderWidth=b=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:j,y:g,width:d-j-h,height:e-g-i};this.plotBox=c.plotBox={x:m,y:l,width:o,height:q};c=pa(t(b,k[3])/2);d=pa(t(b,k[0])/2);this.clipBox={x:c,y:d,width:W(this.plotSizeX-t(b,k[1])/2-c),height:W(this.plotSizeY-t(b,k[2])/2-d)};a||n(this.axes,function(a){a.setAxisSize(); +a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=p(this.optionsMarginTop,a.spacingTop);this.marginRight=p(this.optionsMarginRight,b);this.marginBottom=p(this.optionsMarginBottom,c);this.plotLeft=p(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground, +g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,m=a.plotBackgroundImage,l=a.plotBorderWidth||0,o,q=this.plotLeft,n=this.plotTop,p=this.plotWidth,s=this.plotHeight,x=this.plotBox,r=this.clipRect,t=this.clipBox;o=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-o,d-o));else{e={fill:j||ba};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(o/2,o/2,c-o,d-o,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f? +f.animate(x):this.plotBackground=b.rect(q,n,p,s,0).attr({fill:k}).add().shadow(a.plotShadow);if(m)h?h.animate(x):this.plotBGImage=b.image(m,q,n,p,s).add();r?r.animate({width:t.width,height:t.height}):this.clipRect=b.clipRect(t);if(l)g?g.animate(g.crisp(null,q,n,p,s)):this.plotBorder=b.rect(q,n,p,s,0,l).attr({stroke:a.plotBorderColor,"stroke-width":l,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"], +function(g){c=Q[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=Q[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new Hb(a,d.legend);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()}); +if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(b){var d=y(e.style,b.style),f=C(d.left)+a.plotLeft,g=C(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position); +a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;K(a,"destroy");Ua[a.index]=w;a.renderTo.removeAttribute("data-highcharts-chart");X(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())}); +if(d)d.innerHTML="",X(d),f&&Za(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!ca&&Y==Y.top&&H.readyState!=="complete"||ia&&!Y.canvg?(ia?$b.push(function(){a.firstRender()},a.options.global.canvasToolsURL):H.attachEvent("onreadystatechange",function(){H.detachEvent("onreadystatechange",a.firstRender);H.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),K(a,"init"),a.resetMargins(), +a.setChartSize(),a.propFromSeries(),a.getAxes(),n(b.series||[],function(b){a.initSeries(b)}),K(a,"beforeRender"),a.pointer=new pb(a,b),a.render(),a.renderer.draw(),c&&c.apply(a,[a]),n(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),K(a,"load")}};Sa.prototype.callbacks=[];var Ha=function(){};Ha.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++], +a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ha.prototype.optionsToObject.call(this,a);y(this,a);this.options=this.options?y(this.options,a):a;if(d)this.y=this[d];if(this.x===w&&c)this.x=b===w?c.autoIncrement():b;return this},optionsToObject:function(a){var b,c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b={y:a};else if(Wa(a)){b={};if(a.length>e){c=typeof a[0]; +if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;ga+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions, +d=c[this.type];this.userOptions=a;a=z(d,c.series,a);this.tooltipOptions=z(b.tooltip,a.tooltip);d.marker===null&&delete a.marker;return a},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||S[this.type].color;if(!e&&!a.colorByPoint)v(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols, +c=c.counters;this.symbol=b.symbol;if(!this.symbol)v(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b= +c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).add(f)},addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xData,k=this.yData,m=this.zData,l=this.names,o=g&&g.shift||0,q=e.data;$a(d,i);if(g&&c)g.shift=o+1;if(h){if(c)h.shift=o+1;h.isArea=!0}b=p(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);j.push(d.x);k.push(this.toYData?this.toYData(d):d.y);m.push(d.z);if(l)l[d.x]=d.name;q.push(a);e.legendType==="point"&& +this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),j.shift(),k.shift(),m.shift(),q.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&i.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.chart,f=null,g=this.xAxis,h=g&&g.categories&&!g.categories.length?[]:null,i;this.xIncrement=null;this.pointRange=g&&g.categories?1:d.pointRange;this.colorCounter=0;var j=[],k=[],m=[],l=a?a.length:[],o=(i=this.pointArrayMap)&&i.length,q=!!this.toYData;if(l>(d.turboThreshold|| +1E3)){for(i=0;f===null&&i1&&j[1]k||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]k)b=[],c=[];else if(b[0]k){for(a=0;a=i){e=t(0,a-1);break}for(;ak){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===w||d= +0&&c<=d;)h[c++]=f}this.tooltipPoints=h}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=b.dateTimeLabelFormats,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!c)if(e)for(g in I){if(I[g]>=e){c=d[g];break}}else c=d.day;f&&c&&Ja(a.key)&&(b=b.replace("{point.key}","{point.key:"+c+"}"));return La(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&& +K(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&K(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!da(e))e=S[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h], +e=c[h+"m"],a||(c[h]=a=d.clipRect(y(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip()); +setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,m=this.options.marker,l,o=this.markerGroup;if(m.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=m.enabled&&i.enabled===w||i.enabled,l=c.isInsidePlot(r(d),e,c.inverted),a&&e!==w&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=p(i.symbol,this.symbol),j=i.indexOf("url")=== +0,k)k.attr({visibility:l?ca?"inherit":"visible":"hidden"}).animate(y({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(l&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(o)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=S[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color, +h={stroke:g,fill:g},i=a.points||[],j=[],k,m=a.pointAttrToOptions,l=b.negativeColor,o;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||wa(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);n(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&l)h.color=h.fillColor=l;f=b.colorByPoint||h.color; +if(h.options)for(o in m)v(c[m[o]])&&(f=!0);if(f){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=wa(f.color||h.color).brighten(f.brightness||e.brightness).get();k[""]=a.convertAttribs(y({color:h.color},c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""]);if(h.negative&&b.marker&&l)k[""].fill=k.hover.fill=k.select.fill=a.convertAttribs({fillColor:l}).fill}else k=j;h.pointAttr=k}},update:function(a,b){var c=this.chart,d= +this.type,a=z(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},a);this.remove(!1);y(this,Q[a.type||d].prototype);this.init(c,a);p(b,!0)&&c.redraw(!1)},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Ta),d,e,f=a.data||[],g,h,i;K(a,"destroy");X(a);n(["xAxis","yAxis"],function(b){if(i=a[b])na(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout); +n("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;na(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options.dataLabels,c=a.points,d,e,f,g;if(b.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(b),g=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",b.zIndex||6),e=b,n(c,function(c){var i, +j=c.dataLabel,k,m,l=c.connector,o=!0;d=c.options&&c.options.dataLabels;i=e.enabled||d&&d.enabled;if(j&&!i)c.dataLabel=j.destroy();else if(i){i=b.rotation;b=z(e,d);k=c.getLabelConfig();f=b.format?La(b.format,k):b.formatter.call(k,b);b.style.color=p(b.color,b.style.color,a.color,"black");if(j)if(v(f))j.attr({text:f}),o=!1;else{if(c.dataLabel=j=j.destroy(),l)c.connector=l.destroy()}else if(v(f)){j={fill:b.backgroundColor,stroke:b.borderColor,"stroke-width":b.borderWidth,r:b.borderRadius||0,rotation:i, +padding:b.padding,zIndex:1};for(m in j)j[m]===w&&delete j[m];j=c.dataLabel=a.chart.renderer[i?"text":"label"](f,0,-999,null,null,null,b.useHTML).attr(j).css(b.style).add(g).shadow(b.shadow)}j&&a.alignDataLabel(c,j,b,null,o)}})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=p(a.plotX,-999),a=p(a.plotY,-999),i=b.getBBox(),d=y({x:g?f.plotWidth-a:h,y:r(g?f.plotHeight-h:a),width:0,height:0},d);y(c,{width:i.width,height:i.height});c.rotation?(d={align:c.align,x:d.x+c.x+d.width/2,y:d.y+ +c.y+d.height/2},b[e?"attr":"animate"](d)):b.align(c,null,d);b.attr({visibility:c.crop===!1||f.isInsidePlot(h,a,g)?f.renderer.isSVG?"inherit":"visible":"hidden"})},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;n(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c}, +getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=this.getGraphPath(),g=b.negativeColor;g&&c.push(["graphNeg",g]);n(c,function(c,g){var j=c[0],k=a[j];if(k)hb(k),k.animate({d:f});else if(d&&f.length){k={stroke:c[1],"stroke-width":d,zIndex:1};if(e)k.dashstyle=e;a[j]= +a.chart.renderer.path(f).attr(k).add(a.group).shadow(!g&&b.shadow)}})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=t(e,j);if(d&&(f||g))d=pa(this.yAxis.len-this.yAxis.translate(a.threshold||0)),a={x:0,y:0,width:k,height:d},k={x:0,y:d,width:k,height:k-d},b.inverted&&c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d, +height:e}),this.yAxis.reversed?(b=k,e=a):(b=a,e=k),h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)F(c,"resize",a),F(b,"destroy",function(){X(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b, +c,d,e){var f=this[a],g=!f,h=this.chart,i=this.xAxis,j=this.yAxis;g&&(this[a]=f=h.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"]({translateX:i?i.left:h.plotLeft,translateY:j?j.top:h.plotTop,scaleX:1,scaleY:1});return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup", +"markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis, +e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:p(d&&d.left,a.plotLeft),translateY:p(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&K(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a, +b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===w?!h:a)?"show":"hide";n(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&n(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});n(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();K(c, +f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===w?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;K(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,k=k&&{cursor:k},m=a.singlePoints,l,o=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(l= +e+1;l--;)d[l]==="M"&&d.splice(l+1,0,d[l+1]-i,d[l+2],"L"),(l&&d[l]==="M"||l===e)&&d.splice(l,0,"L",d[l-2]+i,d[l-1]);for(l=0;l=0;d--)da&&i>e?(i=t(a,e),k=2*e-i):ig&&k>e?(k=t(g,e),i=2*e-k):kh?o-h:g-(f.translate(c.y,0,1,0,1)<=g?h:0));c.barX=q;c.pointWidth=i;c.shapeType="rect";c.shapeArgs=c=b.renderer.Element.prototype.crisp.call(0,e,q,n,j,l);e%2&&(c.y-=1,c.height+=1)})},getSymbol:qa,drawLegendSymbol:D.prototype.drawLegendSymbol,drawGraph:qa,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d;n(a.points,function(e){var f=e.plotY,g=e.graphic;if(f!==w&&!isNaN(f)&&e.y!==null)d=e.shapeArgs,g?(hb(g),g.animate(z(d))):e.graphic= +c[e.shapeType](d).attr(e.pointAttr[e.selected?"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius);else if(g)e.graphic=g.destroy()})},drawTracker:function(){var a=this,b=a.chart.pointer,c=a.options.cursor,d=c&&{cursor:c},e=function(b){var c=b.target,d;for(a.onMouseOver();c&&!d;)d=c.point,c=c.parentNode;if(d!==w)d.onMouseOver(b)};n(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});a._hasTracking?a._hasTracking=!0:n(a.trackerGroups, +function(c){if(a[c]&&(a[c].addClass("highcharts-tracker").on("mouseover",e).on("mouseout",function(a){b.onTrackerMouseOut(a)}).css(d),gb))a[c].on("touchstart",e)})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>p(this.translatedThreshold,f.plotSizeY),j=p(c.inside,!!this.options.stacking);if(h&&(d=z(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height: +0,d.height=0);c.align=p(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=p(c.verticalAlign,g||j?"middle":i?"top":"bottom");$.prototype.alignDataLabel.call(this,a,b,c,d,e)},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(ca)a?(e.scaleY=0.001,a=A(b.pos+b.len,t(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)}, +remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});$.prototype.remove.apply(a,arguments)}});Q.column=Z;S.bar=z(S.column);Va=ea(Z,{type:"bar",inverted:!0});Q.bar=Va;S.scatter=z(R,{lineWidth:0,tooltip:{headerFormat:'{series.name}
',pointFormat:"x: {point.x}
y: {point.y}
",followPointer:!0},stickyTracking:!1});Va=ea($,{type:"scatter",sorted:!1,requireSorting:!1, +noSharedTooltip:!0,trackerGroups:["markerGroup"],drawTracker:Z.prototype.drawTracker,setTooltipPoints:qa});Q.scatter=Va;S.pie=z(R,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});R={type:"pie",isCartesian:!1, +pointClass:ea(Ha,{init:function(){Ha.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;y(a,{visible:a.visible!==!1,name:p(a.name,"Slice")});b=function(){a.slice()};F(a,"select",b);F(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===w?!b.visible:a;c.options.data[va(b,c.data)]=b.options;e=a?"show":"hide";n(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b, +a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;$a(c,d.chart);p(b,!0);this.sliced=this.options.sliced=a=v(a)?a:!this.sliced;d.options.data[va(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth", +fill:"color"},getColor:qa,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){$.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();p(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight- +2*c,b=a.center,a=[p(b[0],"50%"),p(b[1],"50%"),a.size||"100%",a.innerSize||0],g=A(e,f),h;return Fa(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*C(a)/100:a)+(d?c:0)})},translate:function(a){this.generatePoints();var b=0,c=0,d=this.options,e=d.slicedOffset,f=e+d.borderWidth,g,h,i,j=this.startAngleRad=bb/180*((d.startAngle||0)%360-90),k=this.points,m=2*bb,l=d.dataLabels.distance,o=d.ignoreHiddenPoint,q,n=k.length,p;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){i= +P.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*ha(i)*(a[2]/2+l)};for(q=0;q0.75*m&&(i-=2*bb);p.slicedTranslation={translateX:r(ha(i)*e),translateY:r(ka(i)*e)};g=ha(i)*a[2]/2;h=ka(i)*a[2]/2;p.tooltipPos=[a[0]+g*0.7,a[1]+h*0.7];p.half=i0,u,s,x,w,z=[[],[]],E,B,y,A,J,C=[0,0,0,0], +db=function(a,b){return b.y-a.y},F=function(a,b){a.sort(function(a,c){return a.angle!==void 0&&(c.angle-a.angle)*b})};if(e.enabled||a._hasPointLabels){$.prototype.drawDataLabels.apply(a);n(b,function(a){a.dataLabel&&z[a.half].push(a)});for(A=0;!w&&b[A];)w=b[A]&&b[A].dataLabel&&(b[A].dataLabel.getBBox().height||21),A++;for(A=2;A--;){var b=[],G=[],I=z[A],H=I.length,D;F(I,A-0.5);if(m>0){for(J=q-o-m;J<=q+o+m;J+=w)b.push(J);s=b.length;if(H>s){c=[].concat(I);c.sort(db);for(J=H;J--;)c[J].rank=J;for(J=H;J--;)I[J].rank>= +s&&I.splice(J,1);H=I.length}for(J=0;J0){if(s=G.pop(),D=s.i,B=s.y,c>B&&b[D+1]!==null||ch-f&&(C[1]=t(r(E+s-h+f),C[1])),B-w/2<0?C[0]=t(r(-B+w/2),C[0]):B+w/2>d&&(C[2]=t(r(B+w/2-d),C[2]))}}if(ua(C)===0||this.verifyDataLabelOverflow(C))this.placeDataLabels(),v&&g&&n(this.points,function(b){i=b.connector;x=b.labelPos;if((u=b.dataLabel)&&u._pos)y=u._attr.visibility,E=u.connX,B=u.connY,j=k?["M",E+(x[6]==="left"?5:-5),B, +"C",E,B,2*x[2]-x[4],2*x[3]-x[5],x[2],x[3],"L",x[4],x[5]]:["M",E+(x[6]==="left"?5:-5),B,"L",x[2],x[3],"L",x[4],x[5]],i?(i.animate({d:j}),i.attr("visibility",y)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:y}).add(a.group);else if(i)b.connector=i.destroy()})}},verifyDataLabelOverflow:function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=t(b[2]-t(a[1],a[3]),c):(e=t(b[2]-a[1]-a[3],c),b[0]+=(a[3]- +a[1])/2);d[1]!==null?e=t(A(e,b[2]-t(a[0],a[2])),c):(e=t(A(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e=c[1]||n===i;)if(j=c.shift(), +k=d.apply(0,l),k!==w&&(g.push(j),h.push(k)),l[0]=[],l[1]=[],l[2]=[],l[3]=[],n===i)break;if(n===i)break;if(o){j=this.cropStart+n;j=e&&e[j]||this.pointClass.prototype.applyOptions.apply({series:this},[f[j]]);var p;for(k=0;kg/i||j&&c.forced){e=!0;this.points=null;a=h.getExtremes();j=a.min;k=a.max;a=h.getGroupIntervalFactor&& +h.getGroupIntervalFactor(j,k,d)||1;g=i*(k-j)/g*a;h=(h.getNonLinearTimeTicks||fb)(zb(g,c.units||bc),j,k,null,d,this.closestPointRange);f=V.groupData.apply(this,[d,f,h,c.approximation]);d=f[0];f=f[1];if(c.smoothed){a=d.length-1;for(d[a]=k;a--&&a>0;)d[a]+=g/2;d[0]=j}this.currentDataGrouping=h.info;if(b.pointRange===null)this.pointRange=h.info.totalRange;this.closestPointRange=h.info.totalRange;this.processedXData=d;this.processedYData=f}else this.currentDataGrouping=null,this.pointRange=m;this.hasGroupedData= +e}};V.destroyGroupedData=function(){var a=this.groupedData;n(a||[],function(b,c){b&&(a[c]=b.destroy?b.destroy():null)});this.groupedData=null};V.generatePoints=function(){gc.apply(this);this.destroyGroupedData();this.groupedData=this.hasGroupedData?this.points:null};V.tooltipHeaderFormatter=function(a){var b=this.tooltipOptions,c=this.options.dataGrouping,d=b.xDateFormat,e,f=this.xAxis,g,h;if(f&&f.options.type==="datetime"&&c&&Ja(a.key)){g=this.currentDataGrouping;c=c.dateTimeLabelFormats;if(g)f= +c[g.unitName],g.count===1?d=f[0]:(d=f[1],e=f[2]);else if(!d&&c)for(h in I)if(I[h]>=f.closestPointRange){d=c[h][0];break}d=ya(d,a.key);e&&(d+=ya(e,a.key+g.totalRange-1));a=b.headerFormat.replace("{point.key}",d)}else a=ic.call(this,a);return a};V.destroy=function(){for(var a=this.groupedData||[],b=a.length;b--;)a[b]&&a[b].destroy();hc.apply(this)};sa(V,"setOptions",function(a,b){var c=a.call(this,b),d=this.type,e=this.chart.options.plotOptions,f=S[d].dataGrouping;if(ac[d])f||(f=z(jc,ac[d])),c.dataGrouping= +z(f,e.series&&e.series.dataGrouping,e[d].dataGrouping,b.dataGrouping);if(this.chart.options._stock)this.requireSorting=!0;return c});S.ohlc=z(S.column,{lineWidth:1,tooltip:{pointFormat:'{series.name}
Open: {point.open}
High: {point.high}
Low: {point.low}
Close: {point.close}
'},states:{hover:{lineWidth:3}},threshold:null});R=ea(Q.column,{type:"ohlc",pointArrayMap:["open","high","low","close"],toYData:function(a){return[a.open, +a.high,a.low,a.close]},pointValKey:"high",pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},upColorProp:"stroke",getAttribs:function(){Q.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,a=a.upColor||this.color,c=z(this.pointAttr),d=this.upColorProp;c[""][d]=a;c.hover[d]=b.hover.upColor||a;c.select[d]=b.select.upColor||a;n(this.points,function(a){if(a.open"},threshold:null,y:-30});Q.flags=ea(Q.column,{type:"flags",sorted:!1,noSharedTooltip:!0,takeOrdinalPosition:!1,forceCrop:!0,init:$.prototype.init,pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth",r:"radius"},translate:function(){Q.column.prototype.translate.apply(this);var a=this.chart,b=this.points,c=b.length-1,d,e,f=this.options.onSeries,f=(d=f&& +a.get(f))&&d.options.step,g=d&&d.points,h=g&&g.length,i=this.xAxis,j=i.getExtremes(),k,m,l;if(d&&d.visible&&h){m=g[h-1].x;for(b.sort(function(a,b){return a.x-b.x});h--&&b[c];)if(d=b[c],k=g[h],k.x<=d.x&&k.plotY!==w){if(d.x<=m)d.plotY=k.plotY,k.x=j.min&&c.x<=j.max?c.plotY=i.lineTop-a.plotTop:c.shapeArgs={};if((e=b[d-1])&&e.plotX===c.plotX){if(e.stackIndex=== +w)e.stackIndex=0;c.stackIndex=e.stackIndex+1}})},drawPoints:function(){var a,b=this.points,c=this.chart.renderer,d,e,f=this.options,g=f.y,h,i,j,k,m,l=f.lineWidth%2/2,o;for(j=b.length;j--;)if(k=b[j],d=k.plotX+l,a=k.stackIndex,h=k.options.shape||f.shape,e=k.plotY,e!==w&&(e=k.plotY+g+l-(a!==w&&a*f.stackDistance)),i=a?w:k.plotX+l,o=a?w:k.plotY,m=k.graphic,e!==w)a=k.pointAttr[k.selected?"select":""],m?m.attr({x:d,y:e,r:a.r,anchorX:i,anchorY:o}):m=k.graphic=c.label(k.options.title||f.title||"A",d,e,h,i, +o,f.useHTML).css(z(f.style,k.style)).attr(a).attr({align:h==="flag"?"left":"center",width:f.width,height:f.height}).add(this.group).shadow(f.shadow),i=m.box,a=i.getBBox(),k.shapeArgs=y(a,{x:d-(h==="flag"?0:i.attr("width")/2),y:e});else if(m)k.graphic=m.destroy()},drawTracker:function(){Q.column.prototype.drawTracker.apply(this);ca&&n(this.points,function(a){a.graphic&&F(a.graphic.element,"mouseover",function(){a.graphic.toFront()})})},animate:qa});xb.flag=function(a,b,c,d,e){var f=e&&e.anchorX||a, +e=e&&e.anchorY||b;return["M",f,e,"L",a,b+d,a,b,a+c,b,a+c,b+d,a,b+d,"M",f,e,"Z"]};n(["circle","square"],function(a){xb[a+"pin"]=function(b,c,d,e,f){var g=f&&f.anchorX,f=f&&f.anchorY,b=xb[a](b,c,d,e);g&&f&&b.push("M",g,c>f?c:c+e,"L",g,f);return b}});cb===ib&&n(["flag","circlepin","squarepin"],function(a){ib.prototype.symbols[a]=xb[a]});R=jb("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#FFF"],[1,"#CCC"]]);D=[].concat(bc);D[4]=[ga,[1,2,3,4]];D[5]=[Ma,[1,2,3]];y(M,{navigator:{handles:{backgroundColor:"#FFF", +borderColor:"#666"},height:40,margin:10,maskFill:"rgba(255, 255, 255, 0.75)",outlineColor:"#444",outlineWidth:1,series:{type:"areaspline",color:"#4572A7",compare:null,fillOpacity:0.4,dataGrouping:{approximation:"average",groupPixelWidth:2,smoothed:!0,units:D},dataLabels:{enabled:!1,zIndex:2},id:"highcharts-navigator-series",lineColor:"#4572A7",lineWidth:1,marker:{enabled:!1},pointRange:0,shadow:!1},xAxis:{tickWidth:0,lineWidth:0,gridLineWidth:1,tickPixelInterval:200,labels:{align:"left",x:3,y:-4}}, +yAxis:{gridLineWidth:0,startOnTick:!1,endOnTick:!1,minPadding:0.1,maxPadding:0.1,labels:{enabled:!1},title:{text:null},tickWidth:0}},scrollbar:{height:ub?20:14,barBackgroundColor:R,barBorderRadius:2,barBorderWidth:1,barBorderColor:"#666",buttonArrowColor:"#666",buttonBackgroundColor:R,buttonBorderColor:"#666",buttonBorderRadius:2,buttonBorderWidth:1,minWidth:6,rifleColor:"#666",trackBackgroundColor:jb("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#EEE"],[1,"#FFF"]]),trackBorderColor:"#CCC", +trackBorderWidth:1,liveRedraw:ca}});Ib.prototype={drawHandle:function(a,b){var c=this.chart,d=c.renderer,e=this.elementsToDestroy,f=this.handles,g=this.navigatorOptions.handles,g={fill:g.backgroundColor,stroke:g.borderColor,"stroke-width":1},h;this.rendered||(f[b]=d.g().css({cursor:"e-resize"}).attr({zIndex:4-b}).add(),h=d.rect(-4.5,0,9,16,3,1).attr(g).add(f[b]),e.push(h),h=d.path(["M",-1.5,4,"L",-1.5,12,"M",0.5,4,"L",0.5,12]).attr(g).add(f[b]),e.push(h));f[b][c.isResizing?"animate":"attr"]({translateX:this.scrollerLeft+ +this.scrollbarHeight+parseInt(a,10),translateY:this.top+this.height/2-8})},drawScrollbarButton:function(a){var b=this.chart.renderer,c=this.elementsToDestroy,d=this.scrollbarButtons,e=this.scrollbarHeight,f=this.scrollbarOptions,g;this.rendered||(d[a]=b.g().add(this.scrollbarGroup),g=b.rect(-0.5,-0.5,e+1,e+1,f.buttonBorderRadius,f.buttonBorderWidth).attr({stroke:f.buttonBorderColor,"stroke-width":f.buttonBorderWidth,fill:f.buttonBackgroundColor}).add(d[a]),c.push(g),g=b.path(["M",e/2+(a?-1:1),e/2- +3,"L",e/2+(a?-1:1),e/2+3,e/2+(a?2:-2),e/2]).attr({fill:f.buttonArrowColor}).add(d[a]),c.push(g));a&&d[a].attr({translateX:this.scrollerWidth-e})},render:function(a,b,c,d){var e=this.chart,f=e.renderer,g,h,i,j,k=this.scrollbarGroup,m=this.navigatorGroup,l=this.scrollbar,m=this.xAxis,o=this.scrollbarTrack,n=this.scrollbarHeight,v=this.scrollbarEnabled,u=this.navigatorOptions,s=this.scrollbarOptions,x=s.minWidth,w=this.height,z=this.top,E=this.navigatorEnabled,B=u.outlineWidth,y=B/2,D=0,J=this.outlineHeight, +I=s.barBorderRadius,H=s.barBorderWidth,F=z+y;if(!isNaN(a)){this.navigatorLeft=g=p(m.left,e.plotLeft+n);this.navigatorWidth=h=p(m.len,e.plotWidth-2*n);this.scrollerLeft=i=g-n;this.scrollerWidth=j=j=h+2*n;if(m.getExtremes){var G=e.xAxis[0].getExtremes(),L=G.dataMin===null,K=m.getExtremes(),M=A(G.dataMin,K.dataMin),G=t(G.dataMax,K.dataMax);!L&&(M!==K.min||G!==K.max)&&m.setExtremes(M,G,!0,!1)}c=p(c,m.translate(a));d=p(d,m.translate(b));this.zoomedMax=a=A(C(t(c,d)),h);this.zoomedMin=d=this.fixedWidth? +a-this.fixedWidth:t(C(A(c,d)),0);this.range=c=a-d;if(!this.rendered){if(E)this.navigatorGroup=m=f.g("navigator").attr({zIndex:3}).add(),this.leftShade=f.rect().attr({fill:u.maskFill}).add(m),this.rightShade=f.rect().attr({fill:u.maskFill}).add(m),this.outline=f.path().attr({"stroke-width":B,stroke:u.outlineColor}).add(m);if(v)this.scrollbarGroup=k=f.g("scrollbar").add(),l=s.trackBorderWidth,this.scrollbarTrack=o=f.rect().attr({y:-l%2/2,fill:s.trackBackgroundColor,stroke:s.trackBorderColor,"stroke-width":l, +r:s.trackBorderRadius||0,height:n}).add(k),this.scrollbar=l=f.rect().attr({y:-H%2/2,height:n,fill:s.barBackgroundColor,stroke:s.barBorderColor,"stroke-width":H,r:I}).add(k),this.scrollbarRifles=f.path().attr({stroke:s.rifleColor,"stroke-width":1}).add(k)}e=e.isResizing?"animate":"attr";E&&(this.leftShade[e]({x:g,y:z,width:d,height:w}),this.rightShade[e]({x:g+a,y:z,width:h-a,height:w}),this.outline[e]({d:["M",i,F,"L",g+d+y,F,g+d+y,F+J-n,"M",g+a-y,F+J-n,"L",g+a-y,F,i+j,F]}),this.drawHandle(d+y,0),this.drawHandle(a+ +y,1));if(v)this.drawScrollbarButton(0),this.drawScrollbarButton(1),k[e]({translateX:i,translateY:r(F+w)}),o[e]({width:j}),g=n+d,h=c-H,h12?"visible":"hidden"})[e]({d:["M",x-3,n/4,"L",x-3,2*n/3,"M",x,n/4,"L",x,2*n/3,"M",x+3,n/4,"L",x+3,2*n/3]});this.scrollbarPad=D;this.rendered=!0}},addEvents:function(){var a=this.chart.container,b=this.mouseDownHandler,c=this.mouseMoveHandler, +d=this.mouseUpHandler,e;e=[[a,"mousedown",b],[a,"mousemove",c],[document,"mouseup",d]];gb&&e.push([a,"touchstart",b],[a,"touchmove",c],[document,"touchend",d]);n(e,function(a){F.apply(null,a)});this._events=e},removeEvents:function(){n(this._events,function(a){X.apply(null,a)});this._events=w;this.navigatorEnabled&&this.baseSeries&&X(this.baseSeries,"updatedData",this.updatedDataHandler)},init:function(){var a=this,b=a.chart,c,d,e=a.scrollbarHeight,f=a.navigatorOptions,g=a.height,h=a.top,i,j,k,m= +document.body.style,l,o=a.baseSeries,n;a.mouseDownHandler=function(d){var d=b.pointer.normalize(d),e=a.zoomedMin,f=a.zoomedMax,h=a.top,i=a.scrollbarHeight,k=a.scrollerLeft,o=a.scrollerWidth,n=a.navigatorLeft,p=a.navigatorWidth,q=a.scrollbarPad,r=a.range,s=d.chartX,u=d.chartY,d=b.xAxis[0],t=ub?10:7;if(u>h&&un+e-q&&sk&&sk+o-i?e+A(10,r):sp&&(f=p-r),f!==e)){a.fixedWidth=r;if(!d.ordinalPositions)d.fixedRange=d.max-d.min;e=c.translate(f,!0);d.setExtremes(e,d.fixedRange?e+d.fixedRange:c.translate(f+r,!0),!0,!1,{trigger:"navigator"})}};a.mouseMoveHandler=function(c){var d=a.scrollbarHeight,e=a.navigatorLeft,f=a.navigatorWidth,g=a.scrollerLeft,h=a.scrollerWidth,i=a.range,l;if(c.pageX!== +0)c=b.pointer.normalize(c),l=c.chartX,lg+h-d&&(l=g+h-d),a.grabbedLeft?(k=!0,a.render(0,0,l-e,a.otherHandlePos)):a.grabbedRight?(k=!0,a.render(0,0,a.otherHandlePos,l-e)):a.grabbedCenter&&(k=!0,lf+j-i&&(l=f+j-i),a.render(0,0,l-j,l-j+i)),k&&a.scrollbarOptions.liveRedraw&&setTimeout(function(){a.mouseUpHandler(c)},0)};a.mouseUpHandler=function(d){k&&b.xAxis[0].setExtremes(c.translate(a.zoomedMin,!0),c.translate(a.zoomedMax,!0),!0,!1,{trigger:"navigator",DOMEvent:d});if(d.type!=="mousemove")a.grabbedLeft= +a.grabbedRight=a.grabbedCenter=a.fixedWidth=k=j=null,m.cursor=l||""};a.updatedDataHandler=function(){var c=o.xAxis,d=c.getExtremes(),e=d.min,f=d.max,g=d.dataMin,d=d.dataMax,h=f-e,j,k,l,m,p;j=i.xData;var r=!!c.setExtremes;k=f>=j[j.length-1];j=e<=g;if(!n)i.options.pointStart=o.xData[0],i.setData(o.options.data,!1),p=!0;j&&(m=g,l=m+h);k&&(l=d,j||(m=t(l-h,i.xData[0])));r&&(j||k)?c.setExtremes(m,l,!0,!1,{trigger:"updatedData"}):(p&&b.redraw(!1),a.render(t(e,g),A(f,d)))};var r=b.xAxis.length,u=b.yAxis.length; +b.extraBottomMargin=a.outlineHeight+f.margin;if(a.navigatorEnabled){var s=o?o.options:{},x=s.data,v=f.series;n=v.data;a.xAxis=c=new Da(b,z({ordinal:o&&o.xAxis.options.ordinal},f.xAxis,{isX:!0,type:"datetime",index:r,height:g,offset:0,offsetLeft:e,offsetRight:-e,startOnTick:!1,endOnTick:!1,minPadding:0,maxPadding:0,zoomEnabled:!1}));a.yAxis=d=new Da(b,z(f.yAxis,{alignTicks:!1,height:g,offset:0,index:u,zoomEnabled:!1}));r=z(s,v,{threshold:null,clip:!1,enableMouseTracking:!1,group:"nav",padXAxis:!1, +xAxis:r,yAxis:u,name:"Navigator",showInLegend:!1,isInternal:!0,visible:!0});r.data=n||x;i=b.initSeries(r);if(o&&f.adaptToUpdatedData!==!1)F(o,"updatedData",a.updatedDataHandler),o.userOptions.events=y(o.userOptions.event,{updatedData:a.updatedDataHandler})}else a.xAxis=c={translate:function(a,c){var d=b.xAxis[0].getExtremes(),f=b.plotWidth-2*e,g=d.dataMin,d=d.dataMax-g;return c?a*d/f+g:f*(a-g)/d}};a.series=i;sa(b,"getMargins",function(b){var e=this.legend,f=e.options;b.call(this);a.top=h=a.navigatorOptions.top|| +this.chartHeight-a.height-a.scrollbarHeight-this.options.chart.spacingBottom-(f.verticalAlign==="bottom"&&f.enabled&&!f.floating?e.legendHeight+p(f.margin,10):0);if(c&&d)c.options.top=d.options.top=h,c.setAxisSize(),d.setAxisSize()});a.addEvents()},destroy:function(){this.removeEvents();n([this.xAxis,this.yAxis,this.leftShade,this.rightShade,this.outline,this.scrollbarTrack,this.scrollbarRifles,this.scrollbarGroup,this.scrollbar],function(a){a&&a.destroy&&a.destroy()});this.xAxis=this.yAxis=this.leftShade= +this.rightShade=this.outline=this.scrollbarTrack=this.scrollbarRifles=this.scrollbarGroup=this.scrollbar=null;n([this.scrollbarButtons,this.handles,this.elementsToDestroy],function(a){Aa(a)})}};Highcharts.Scroller=Ib;sa(Da.prototype,"zoom",function(a,b,c){var d=this.chart,e=d.options,f=e.chart.zoomType,g=e.navigator,e=e.rangeSelector,h;if(this.isXAxis&&(g&&g.enabled||e&&e.enabled))if(f==="x")d.resetZoomButton="blocked";else if(f==="y")h=!1;else if(f==="xy")d=this.previousZoom,v(b)?this.previousZoom= +[this.min,this.max]:d&&(b=d[0],c=d[1],delete this.previousZoom);return h!==w?h:a.call(this,b,c)});sa(Sa.prototype,"init",function(a,b,c){F(this,"beforeRender",function(){var a=this.options;if(a.navigator.enabled||a.scrollbar.enabled)this.scroller=new Ib(this)});a.call(this,b,c)});y(M,{rangeSelector:{buttonTheme:{width:28,height:16,padding:1,r:0,stroke:"#68A",zIndex:7},inputPosition:{align:"right"},labelStyle:{color:"#666"}}});M.lang=z(M.lang,{rangeSelectorZoom:"Zoom",rangeSelectorFrom:"From",rangeSelectorTo:"To"}); +Jb.prototype={clickButton:function(a,b,c){var d=this,e=d.chart,f=d.buttons,g=e.xAxis[0],h=g&&g.getExtremes(),i=e.scroller&&e.scroller.xAxis,j=i&&i.getExtremes&&i.getExtremes(),i=j&&j.dataMin,j=j&&j.dataMax,k=h&&h.dataMin,m=h&&h.dataMax,l=(v(k)&&v(i)?A:p)(k,i),o=(v(m)&&v(j)?t:p)(m,j),q,h=g&&A(h.max,p(o,h.max)),i=new Date(h),j=b.type,k=b.count,r,u,m={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5};if(!(l===null||o===null||a===d.selected)){if(m[j])r=m[j]*k,q=t(h-r,l);else if(j=== +"month"||j==="year")r={month:"Month",year:"FullYear"}[j],i["set"+r](i["get"+r]()-k),q=t(i.getTime(),p(l,Number.MIN_VALUE)),r={month:30,year:365}[j]*864E5*k;else if(j==="ytd")if(g){if(o===w)l=Number.MAX_VALUE,o=Number.MIN_VALUE,n(e.series,function(a){a=a.xData;l=A(a[0],l);o=t(a[a.length-1],o)}),c=!1;h=new Date(o);u=h.getFullYear();q=u=t(l||0,Date.UTC(u,0,1));h=h.getTime();h=A(o||h,h)}else{F(e,"beforeRender",function(){d.clickButton(a,b)});return}else j==="all"&&g&&(q=l,h=o);f[a]&&f[a].setState(2); +g?g.setExtremes(q,h,p(c,1),0,{trigger:"rangeSelectorButton",rangeSelectorButton:b}):(c=e.options.xAxis,c[0]=z(c[0],{range:r,min:u}));d.selected=a}},defaultButtons:[{type:"month",count:1,text:"1m"},{type:"month",count:3,text:"3m"},{type:"month",count:6,text:"6m"},{type:"ytd",text:"YTD"},{type:"year",count:1,text:"1y"},{type:"all",text:"All"}],init:function(a){var b=this,c=a.options.rangeSelector,d=c.buttons||[].concat(b.defaultButtons),e=b.buttons=[],c=c.selected,f=b.blurInputs=function(){var a=b.minInput, +c=b.maxInput;a&&a.blur();c&&c.blur()};b.chart=a;a.extraTopMargin=25;b.buttonOptions=d;F(a.container,"mousedown",f);F(a,"resize",f);c!==w&&d[c]&&this.clickButton(c,d[c],!1);F(a,"load",function(){F(a.xAxis[0],"afterSetExtremes",function(){if(this.fixedRange!==this.max-this.min)e[b.selected]&&!a.renderer.forExport&&e[b.selected].setState(0),b.selected=null;this.fixedRange=null})})},setInputValue:function(a,b){var c=this.chart.options.rangeSelector;if(b)this[a+"Input"].HCTime=b;this[a+"Input"].value= +ya(c.inputEditDateFormat||"%Y-%m-%d",this[a+"Input"].HCTime);this[a+"DateBox"].attr({text:ya(c.inputDateFormat||"%b %e, %Y",this[a+"Input"].HCTime)})},drawInput:function(a){var b=this,c=b.chart,d=c.options.chart.style,e=c.renderer,f=c.options.rangeSelector,g=b.div,h=a==="min",i,j,k,m=this.inputGroup;this[a+"Label"]=j=e.label(M.lang[h?"rangeSelectorFrom":"rangeSelectorTo"],this.inputGroup.offset).attr({padding:1}).css(z(d,f.labelStyle)).add(m);m.offset+=j.width+5;this[a+"DateBox"]=k=e.label("",m.offset).attr({padding:1, +width:90,height:16,stroke:"silver","stroke-width":1}).css(z({textAlign:"center"},d,f.inputStyle)).on("click",function(){b[a+"Input"].focus()}).add(m);m.offset+=k.width+(h?10:0);this[a+"Input"]=i=aa("input",{name:a,className:"highcharts-range-selector",type:"text"},y({position:"absolute",border:0,width:"1px",height:"1px",padding:0,textAlign:"center",fontSize:d.fontSize,fontFamily:d.fontFamily,top:c.plotTop+"px"},f.inputStyle),g);i.onfocus=function(){L(this,{left:m.translateX+k.x+"px",top:m.translateY+ +"px",width:k.width-2+"px",height:k.height-2+"px",border:"2px solid silver"})};i.onblur=function(){L(this,{border:0,width:"1px",height:"1px"});b.setInputValue(a)};i.onchange=function(){var a=i.value,d=Date.parse(a),e=c.xAxis[0].getExtremes();isNaN(d)&&(d=a.split("-"),d=Date.UTC(C(d[0]),C(d[1])-1,C(d[2])));if(!isNaN(d)&&(M.global.useUTC||(d+=(new Date).getTimezoneOffset()*6E4),h&&d>=e.dataMin&&d<=b.maxInput.HCTime||!h&&d<=e.dataMax&&d>=b.minInput.HCTime))c.xAxis[0].setExtremes(h?d:e.min,h?e.max:d,w, +w,{trigger:"rangeSelectorInput"})}},render:function(a,b){var c=this,d=c.chart,e=d.renderer,f=d.container,g=d.options,h=g.exporting&&d.options.navigation.buttonOptions,i=g.rangeSelector,j=c.buttons,k=M.lang,m=c.div,m=c.inputGroup,l=i.buttonTheme,o=i.inputEnabled!==!1,p=l&&l.states,r=d.plotLeft,u;if(!c.rendered&&(c.zoomText=e.text(k.rangeSelectorZoom,r,d.plotTop-10).css(i.labelStyle).add(),u=r+c.zoomText.getBBox().width+5,n(c.buttonOptions,function(a,b){j[b]=e.button(a.text,u,d.plotTop-25,function(){c.clickButton(b, +a);c.isActive=!0},l,p&&p.hover,p&&p.select).css({textAlign:"center"}).add();u+=j[b].width+(i.buttonSpacing||0);c.selected===b&&j[b].setState(2)}),o))c.div=m=aa("div",null,{position:"relative",height:0,zIndex:1}),f.parentNode.insertBefore(m,f),c.inputGroup=m=e.g("input-group").add(),m.offset=0,c.drawInput("min"),c.drawInput("max");o&&(f=d.plotTop-35,m.align(y({y:f,width:m.offset,x:h&&f<(h.y||0)+h.height-g.chart.spacingTop?-40:0},i.inputPosition),!0,d.spacingBox),c.setInputValue("min",a),c.setInputValue("max", +b));c.rendered=!0},destroy:function(){var a=this.minInput,b=this.maxInput,c=this.chart,d=this.blurInputs,e;X(c.container,"mousedown",d);X(c,"resize",d);Aa(this.buttons);if(a)a.onfocus=a.onblur=a.onchange=null;if(b)b.onfocus=b.onblur=b.onchange=null;for(e in this)this[e]&&e!=="chart"&&(this[e].destroy?this[e].destroy():this[e].nodeType&&Za(this[e])),this[e]=null}};sa(Sa.prototype,"init",function(a,b,c){F(this,"init",function(){if(this.options.rangeSelector.enabled)this.rangeSelector=new Jb(this)}); +a.call(this,b,c)});Highcharts.RangeSelector=Jb;Sa.prototype.callbacks.push(function(a){function b(){f=a.xAxis[0].getExtremes();g.render(t(f.min,f.dataMin),A(f.max,p(f.dataMax,Number.MAX_VALUE)))}function c(){f=a.xAxis[0].getExtremes();h.render(f.min,f.max)}function d(a){g.render(a.min,a.max)}function e(a){h.render(a.min,a.max)}var f,g=a.scroller,h=a.rangeSelector;g&&(F(a.xAxis[0],"afterSetExtremes",d),sa(a,"drawChartBox",function(a){var c=this.isDirtyBox;a.call(this);c&&b()}),b());h&&(F(a.xAxis[0], +"afterSetExtremes",e),F(a,"resize",c),c());F(a,"destroy",function(){g&&X(a.xAxis[0],"afterSetExtremes",d);h&&(X(a,"resize",c),X(a.xAxis[0],"afterSetExtremes",e))})});Highcharts.StockChart=function(a,b){var c=a.series,d,e={marker:{enabled:!1,states:{hover:{radius:5}}},states:{hover:{lineWidth:2}}},f={shadow:!1,borderWidth:0};a.xAxis=Fa(ja(a.xAxis||{}),function(a){return z({minPadding:0,maxPadding:0,ordinal:!0,title:{text:null},labels:{overflow:"justify"},showLastLabel:!0},a,{type:"datetime",categories:null})}); +a.yAxis=Fa(ja(a.yAxis||{}),function(a){d=a.opposite;return z({labels:{align:d?"right":"left",x:d?-2:2,y:-2},showLastLabel:!1,title:{text:null}},a)});a.series=null;a=z({chart:{panning:!0,pinchType:"x"},navigator:{enabled:!0},scrollbar:{enabled:!0},rangeSelector:{enabled:!0},title:{text:null},tooltip:{shared:!0,crosshairs:!0},legend:{enabled:!1},plotOptions:{line:e,spline:e,area:e,areaspline:e,arearange:e,areasplinerange:e,column:f,columnrange:f,candlestick:f,ohlc:f}},a,{_stock:!0,chart:{inverted:!1}}); +a.series=c;return new Sa(a,b)};sa(pb.prototype,"init",function(a,b,c){var d=c.chart.pinchType||"";a.call(this,b,c);this.pinchX=this.pinchHor=d.indexOf("x")!==-1;this.pinchY=this.pinchVert=d.indexOf("y")!==-1});var kc=V.init,lc=V.processData,mc=Ha.prototype.tooltipFormatter;V.init=function(){kc.apply(this,arguments);this.setCompare(this.options.compare)};V.setCompare=function(a){this.modifyValue=a==="value"||a==="percent"?function(b,c){var d=this.compareValue,b=a==="value"?b-d:b=100*(b/d)-100;if(c)c.change= +b;return b}:null;if(this.chart.hasRendered)this.isDirty=!0};V.processData=function(){lc.apply(this,arguments);if(this.options.compare)for(var a=0,b=this.processedXData,c=this.processedYData,d=c.length,e=this.xAxis.getExtremes().min;a=e){this.compareValue=c[a];break}};Da.prototype.setCompare=function(a,b){this.isXAxis||(n(this.series,function(b){b.setCompare(a)}),p(b,!0)&&this.chart.redraw())};Ha.prototype.tooltipFormatter=function(a){a=a.replace("{point.change}", +(this.change>0?"+":"")+xa(this.change,this.series.tooltipOptions.changeDecimals||2));return mc.apply(this,[a])};(function(){var a=V.init,b=V.getSegments;V.init=function(){var b,d;a.apply(this,arguments);b=this.chart;(d=this.xAxis)&&d.options.ordinal&&F(this,"updatedData",function(){delete d.ordinalIndex});if(d&&d.options.ordinal&&!d.hasOrdinalExtension){d.hasOrdinalExtension=!0;d.beforeSetTickPositions=function(){var a,b=[],c=!1,e,j=this.getExtremes(),k=j.min,j=j.max,m;if(this.options.ordinal){n(this.series, +function(c,d){if(c.visible!==!1&&c.takeOrdinalPosition!==!1&&(b=b.concat(c.processedXData),a=b.length,b.sort(function(a,b){return a-b}),a))for(d=a-1;d--;)b[d]===b[d+1]&&b.splice(d,1)});a=b.length;if(a>2){e=b[1]-b[0];for(m=a-1;m--&&!c;)b[m+1]-b[m]!==e&&(c=!0)}c?(this.ordinalPositions=b,c=d.val2lin(k,!0),e=d.val2lin(j,!0),this.ordinalSlope=j=(j-k)/(e-c),this.ordinalOffset=k-c*j):this.ordinalPositions=this.ordinalSlope=this.ordinalOffset=w}};d.val2lin=function(a,b){var c=this.ordinalPositions;if(c){var d= +c.length,e,k;for(e=d;e--;)if(c[e]===a){k=e;break}for(e=d-1;e--;)if(a>c[e]||e===0){c=(a-c[e])/(c[e+1]-c[e]);k=e+c;break}return b?k:this.ordinalSlope*(k||0)+this.ordinalOffset}else return a};d.lin2val=function(a,b){var c=this.ordinalPositions;if(c){var d=this.ordinalSlope,e=this.ordinalOffset,k=c.length-1,m,l;if(b)a<0?a=c[0]:a>k?a=c[k]:(k=W(a),l=a-k);else for(;k--;)if(m=d*k+e,a>=m){d=d*(k+1)+e;l=(a-m)/(d-m);break}return l!==w&&c[k]!==w?c[k]+(l?l*(c[k+1]-c[k]):0):a}else return a};d.getExtendedPositions= +function(){var a=d.series[0].currentDataGrouping,e=d.ordinalIndex,h=a?a.count+a.unitName:"raw",i=d.getExtremes(),j,k;if(!e)e=d.ordinalIndex={};if(!e[h])j={series:[],getExtremes:function(){return{min:i.dataMin,max:i.dataMax}},options:{ordinal:!0}},n(d.series,function(d){k={xAxis:j,xData:d.xData,chart:b,destroyGroupedData:qa};k.options={dataGrouping:a?{enabled:!0,forced:!0,approximation:"open",units:[[a.unitName,[a.count]]]}:{enabled:!1}};d.processData.apply(k);j.series.push(k)}),d.beforeSetTickPositions.apply(j), +e[h]=j.ordinalPositions;return e[h]};d.getGroupIntervalFactor=function(a,b,c){for(var d=0,e=c.length,k=[];dc;j[n]k*5||t){if(j[n]>z){for(p=fb(a,j[l],j[n],e);p.length&&p[0]<=z;)p.shift();p.length&&(z=p[p.length-1]);y=y.concat(p)}l=n+1}if(t)break}a=p.info;if(m&&a.unitRange<=I[za]){n=y.length-1;for(l=1;lc?a-1:a;for(B=void 0;m--;)l=n[m],c=B-l,B&&c +1)o&&n(o,function(a){a.setState()}),i<0?(o=r,r=d.ordinalPositions?d:r):o=d.ordinalPositions?d:r,u=r.ordinalPositions,k>u[u.length-1]&&u.push(k),o=p.apply(o,[s.apply(o,[m,!0])+i,!0]),i=p.apply(r,[s.apply(r,[l,!0])+i,!0]),o>A(j.dataMin,m)&&ia.xAxis.closestPointRange*e&&d.splice(g+1,0,b.splice(h+1,b.length-h))})}})();y(Highcharts,{Axis:Da,Chart:Sa,Color:wa,Legend:Hb,Pointer:pb,Point:Ha,Tick:ab,Tooltip:Gb,Renderer:cb,Series:$,SVGElement:Ca,SVGRenderer:Ga,arrayMin:Pa,arrayMax:ua,charts:Ua,dateFormat:ya,format:La,pathAnim:Lb,getOptions:function(){return M},hasBidiBug:cc,isTouchDevice:ub,numberFormat:xa,seriesTypes:Q,setOptions:function(a){M=z(M,a);Tb();return M},addEvent:F,removeEvent:X,createElement:aa,discardElement:Za, +css:L,each:n,extend:y,map:Fa,merge:z,pick:p,splat:ja,extendClass:ea,pInt:C,wrap:sa,svg:ca,canvas:ia,vml:!ca&&!ia,product:"Highstock",version:"1.3.2"})})(); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/area/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/area/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/area/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/area/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/areaspline/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/areaspline/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/areaspline/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/areaspline/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/basic-line/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/basic-line/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/basic-line/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/basic-line/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/candlestick/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/candlestick/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/candlestick/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/candlestick/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/candlestick-and-volume/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/candlestick-and-volume/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/candlestick-and-volume/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/candlestick-and-volume/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/column/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/column/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/column/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/column/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/compare/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/compare/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/compare/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/compare/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/data-grouping/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/data-grouping/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/data-grouping/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/data-grouping/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/dynamic-update/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/dynamic-update/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/dynamic-update/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/dynamic-update/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-general/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-general/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-general/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-general/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-placement/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-placement/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-placement/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-placement/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-shapes/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-shapes/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/flags-shapes/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/flags-shapes/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,102 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/intraday-area/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/intraday-area/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/intraday-area/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/intraday-area/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ - - - - - Highstock Example - - - - - -
- - - - - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/intraday-candlestick/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/intraday-candlestick/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/intraday-candlestick/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/intraday-candlestick/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ - - - - - Highstock Example - - - - - -
- - - - - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/lazy-loading/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/lazy-loading/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/lazy-loading/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/lazy-loading/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,121 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- -
In this example Highcharts' data grouping is turned off. Instead, new data - with a suitable resolution is loaded from the server every time the - extremes of the x axis change.
- - -
-To do lazy-loading
-
-- Range selector buttons not taking the pressed state. This, along with the minPadding and maxPadding issue, should be solved by preventing setData from resetting the extremes on the x axis when there are userMin and userMax
-- loading data after Sep 30 is impossible though there is data on the server
-- fails on ordinal when zooming in too narrow
-- Make cache tables on the server
-
-Problem 1:
-This code, in StockNavigation.js, always runs because stickToMin is always true because we are loading data within the previous range. This causes the second
-setExtremes: 
-- baseXAxis.setExtremes(newMin, newMax, true, false);
-
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/line-markers/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/line-markers/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/line-markers/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/line-markers/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/markers-only/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/markers-only/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/markers-only/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/markers-only/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/navigator-disabled/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/navigator-disabled/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/navigator-disabled/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/navigator-disabled/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/ohlc/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/ohlc/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/ohlc/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/ohlc/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/scrollbar-disabled/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/scrollbar-disabled/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/scrollbar-disabled/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/scrollbar-disabled/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/spline/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/spline/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/spline/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/spline/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/step-line/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/step-line/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/step-line/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/step-line/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/styled-scrollbar/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/styled-scrollbar/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/styled-scrollbar/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/styled-scrollbar/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-plotbands/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-plotbands/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-plotbands/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-plotbands/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-plotlines/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-plotlines/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-plotlines/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-plotlines/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-reversed/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-reversed/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/examples/yaxis-reversed/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/examples/yaxis-reversed/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ - - - - - Highstock Example - - - - - - - - -
- - diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/exporting-server/index.php openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/exporting-server/index.php --- openplacos-0.3+560~lucid1/server/public/lib/highstock/exporting-server/index.php 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/exporting-server/index.php 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -$output"; - echo "Error while converting SVG. "; - - if (strpos($output, 'SVGConverter.error.while.rasterizing.file') !== false) { - echo " -

Debug steps

-
    -
  1. Copy the SVG:
  2. -
  3. Go to validator.w3.org/#validate_by_input
  4. -
  5. Paste the SVG
  6. -
  7. Click More Options and select SVG 1.1 for Use Doctype
  8. -
  9. Click the Check button
  10. -
"; - } - } - - // stream it - else { - header("Content-Disposition: attachment; filename=\"$filename.$ext\""); - header("Content-Type: $type"); - echo file_get_contents($outfile); - } - - // delete it - unlink("temp/$tempName.svg"); - unlink($outfile); - -// SVG can be streamed directly back -} else if ($ext == 'svg') { - header("Content-Disposition: attachment; filename=\"$filename.$ext\""); - header("Content-Type: $type"); - echo $svg; - -} else { - echo "Invalid type"; -} -?> Binary files /tmp/sXDfjUaDrJ/openplacos-0.3+560~lucid1/server/public/lib/highstock/gfx/vml-radial-gradient.png and /tmp/n4VTy07VW9/openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/gfx/vml-radial-gradient.png differ Binary files /tmp/sXDfjUaDrJ/openplacos-0.3+560~lucid1/server/public/lib/highstock/Highstock-1.1.6.zip and /tmp/n4VTy07VW9/openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/Highstock-1.1.6.zip differ diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/index.htm openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/index.htm --- openplacos-0.3+560~lucid1/server/public/lib/highstock/index.htm 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/index.htm 2015-01-29 17:00:42.000000000 +0000 @@ -12,6 +12,7 @@
  • Two panes, candlestick and volume
  • Compare multiple series
  • 52,000 points with data grouping
  • +
  • 1.7 million points with async loading
  • Intraday area
  • Intraday candlestick
  • Flags marking events
  • @@ -23,10 +24,13 @@
  • Spline
  • Step line
  • Area
  • -
  • Areaspline
  • +
  • Area spline
  • +
  • Area range
  • +
  • Area spline range
  • Candlestick
  • OHLC
  • Column
  • +
  • Column range
  • Point markers only
  • Various features

    diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/mootools-adapter.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/mootools-adapter.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/mootools-adapter.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/mootools-adapter.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,13 +1,13 @@ /* - Highstock JS v1.1.6 (2012-06-08) + Highstock JS v1.3.2 (2013-06-05) MooTools adapter - (c) 2010-2011 Torstein H?nsi + (c) 2010-2013 Torstein Hønsi License: www.highcharts.com/license */ -(function(){var e=window,i=document,f=e.MooTools.version.substring(0,3),g=f==="1.2"||f==="1.1",j=g||f==="1.3",h=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this, -arguments)}},adapterRun:function(a,b){return $(a).getStyle(b).toInt()},getScript:function(a,b){var c=i.getElementsByTagName("head")[0],d=i.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var b=arguments;a.attr.call(a,b[0],b[1][0])},a.$family=function(){return!0};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a),h({transition:Fx.Transitions.Quad.easeInOut}, -c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return g?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},merge:function(){var a=arguments,b=[{}],c=a.length;if(g)a=$merge.apply(null,a);else{for(;c--;)typeof a[c]!=="boolean"&&(b[c+1]=a[c]);a=Object.merge.apply(Object,b)}return a},offset:function(a){a=$(a).getOffsets();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName? -$(a):h(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b==="unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&(e.HighchartsAdapter.extendWithEvents(a),b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=h(b,c);b.preventDefault=function(){d=null};a.fireEvent&& -a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){a.pageX=a.page.x;a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})(); +(function(){var e=window,h=document,f=e.MooTools.version.substring(0,3),i=f==="1.2"||f==="1.1",j=i||f==="1.3",g=e.$extend||function(){return Object.append.apply(Object,arguments)};e.HighchartsAdapter={init:function(a){var b=Fx.prototype,c=b.start,d=Fx.Morph.prototype,e=d.compute;b.start=function(b,d){var e=this.element;if(b.d)this.paths=a.init(e,e.d,this.toD);c.apply(this,arguments);return this};d.compute=function(b,c,d){var f=this.paths;if(f)this.element.attr("d",a.step(f[0],f[1],d,this.toD));else return e.apply(this, +arguments)}},adapterRun:function(a,b){if(b==="width"||b==="height")return parseInt($(a).getStyle(b),10)},getScript:function(a,b){var c=h.getElementsByTagName("head")[0],d=h.createElement("script");d.type="text/javascript";d.src=a;d.onload=b;c.appendChild(d)},animate:function(a,b,c){var d=a.attr,f=c&&c.complete;if(d&&!a.setStyle)a.getStyle=a.attr,a.setStyle=function(){var a=arguments;this.attr.call(this,a[0],a[1][0])},a.$family=function(){return!0};e.HighchartsAdapter.stop(a);c=new Fx.Morph(d?a:$(a), +g({transition:Fx.Transitions.Quad.easeInOut},c));if(d)c.element=a;if(b.d)c.toD=b.d;f&&c.addEvent("complete",f);c.start(b);a.fx=c},each:function(a,b){return i?$each(a,b):Array.each(a,b)},map:function(a,b){return a.map(b)},grep:function(a,b){return a.filter(b)},inArray:function(a,b,c){return b?b.indexOf(a,c):-1},offset:function(a){a=a.getPosition();return{left:a.x,top:a.y}},extendWithEvents:function(a){a.addEvent||(a.nodeName?$(a):g(a,new Events))},addEvent:function(a,b,c){typeof b==="string"&&(b=== +"unload"&&(b="beforeunload"),e.HighchartsAdapter.extendWithEvents(a),a.addEvent(b,c))},removeEvent:function(a,b,c){typeof a!=="string"&&a.addEvent&&(b?(b==="unload"&&(b="beforeunload"),c?a.removeEvent(b,c):a.removeEvents&&a.removeEvents(b)):a.removeEvents())},fireEvent:function(a,b,c,d){b={type:b,target:a};b=j?new Event(b):new DOMEvent(b);b=g(b,c);if(!b.target&&b.event)b.target=b.event.target;b.preventDefault=function(){d=null};a.fireEvent&&a.fireEvent(b.type,b);d&&d(b)},washMouseEvent:function(a){if(a.page)a.pageX= +a.page.x,a.pageY=a.page.y;return a},stop:function(a){a.fx&&a.fx.cancel()}}})(); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/mootools-adapter.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/mootools-adapter.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/mootools-adapter.src.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/mootools-adapter.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,8 +1,8 @@ /** - * @license Highstock JS v1.1.6 (2012-06-08) + * @license Highstock JS v1.3.2 (2013-06-05) * MooTools adapter * - * (c) 2010-2011 Torstein Hønsi + * (c) 2010-2013 Torstein Hønsi * * License: www.highcharts.com/license */ @@ -78,8 +78,9 @@ // This currently works for getting inner width and height. If adding // more methods later, we need a conditional implementation for each. - return $(el).getStyle(method).toInt(); - + if (method === 'width' || method === 'height') { + return parseInt($(el).getStyle(method), 10); + } }, /** @@ -115,7 +116,7 @@ el.getStyle = el.attr; el.setStyle = function () { // property value is given as array in Moo - break it down var args = arguments; - el.attr.call(el, args[0], args[1][0]); + this.attr.call(this, args[0], args[1][0]); }; // dirty hack to trick Moo into handling el as an element wrapper el.$family = function () { return true; }; @@ -181,37 +182,19 @@ grep: function (arr, fn) { return arr.filter(fn); }, - + /** - * Deep merge two objects and return a third + * Return the index of an item in an array, or -1 if not matched */ - merge: function () { - var args = arguments, - args13 = [{}], // MooTools 1.3+ - i = args.length, - ret; - - if (legacy) { - ret = $merge.apply(null, args); - } else { - while (i--) { - // Boolean argumens should not be merged. - // JQuery explicitly skips this, so we do it here as well. - if (typeof args[i] !== 'boolean') { - args13[i + 1] = args[i]; - } - } - ret = Object.merge.apply(Object, args13); - } - - return ret; + inArray: function (item, arr, from) { + return arr ? arr.indexOf(item, from) : -1; }, /** * Get the offset of an element relative to the top left corner of the web page */ offset: function (el) { - var offsets = $(el).getOffsets(); + var offsets = el.getPosition(); // #1496 return { left: offsets.x, top: offsets.y @@ -258,19 +241,20 @@ return; } - win.HighchartsAdapter.extendWithEvents(el); - if (type) { - if (type === 'unload') { // Moo self destructs before custom unload events - type = 'beforeunload'; - } - - if (fn) { - el.removeEvent(type, fn); - } else if (el.removeEvents) { // #958 - el.removeEvents(type); + if (el.addEvent) { // If el doesn't have an addEvent method, there are no events to remove + if (type) { + if (type === 'unload') { // Moo self destructs before custom unload events + type = 'beforeunload'; + } + + if (fn) { + el.removeEvent(type, fn); + } else if (el.removeEvents) { // #958 + el.removeEvents(type); + } + } else { + el.removeEvents(); } - } else { - el.removeEvents(); } }, @@ -282,6 +266,12 @@ // create an event object that keeps all functions event = legacyEvent ? new Event(eventArgs) : new DOMEvent(eventArgs); event = $extend(event, eventArguments); + + // When running an event on the Chart.prototype, MooTools nests the target in event.event + if (!event.target && event.event) { + event.target = event.event.target; + } + // override the preventDefault function to be able to use // this for custom events event.preventDefault = function () { @@ -300,11 +290,13 @@ }, /** - * Set back e.pageX and e.pageY that MooTools has abstracted away + * Set back e.pageX and e.pageY that MooTools has abstracted away. #1165, #1346. */ washMouseEvent: function (e) { - e.pageX = e.page.x; - e.pageY = e.page.y; + if (e.page) { + e.pageX = e.page.x; + e.pageY = e.page.y; + } return e; }, diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/prototype-adapter.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/prototype-adapter.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/adapters/prototype-adapter.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/adapters/prototype-adapter.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,16 +1,15 @@ /* - Highstock JS v1.1.6 (2012-06-08) + Highstock JS v1.3.2 (2013-06-05) Prototype adapter - @author Michael Nelson, Torstein H?nsi. + @author Michael Nelson, Torstein Hønsi. Feel free to use and modify this script. Highcharts license: www.highcharts.com/license. */ -var HighchartsAdapter=function(){var g=typeof Effect!=="undefined";return{init:function(c){if(g)Effect.HighchartsTransition=Class.create(Effect.Base,{initialize:function(a,b,d,e){var f;this.element=a;this.key=b;f=a.attr?a.attr(b):$(a).getStyle(b);if(b==="d")this.paths=c.init(a,a.d,d),this.toD=d,f=0,d=1;this.start(Object.extend(e||{},{from:f,to:d,attribute:b}))},setup:function(){HighchartsAdapter._extend(this.element);if(!this.element._highchart_animation)this.element._highchart_animation={};this.element._highchart_animation[this.key]= -this},update:function(a){var b=this.paths,d=this.element;b&&(a=c.step(b[0],b[1],a,this.toD));d.attr?d.attr(this.options.attribute,a):(b={},b[this.options.attribute]=a,$(d).setStyle(b))},finish:function(){delete this.element._highchart_animation[this.key]}})},adapterRun:function(c,a){return parseInt($(c).getStyle(a),10)},getScript:function(c,a){var b=$$("head")[0];b&&b.appendChild((new Element("script",{type:"text/javascript",src:c})).observe("load",a))},addNS:function(c){var a=/^(?:click|mouse(?:down|up|over|move|out))$/; -return/^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/.test(c)||a.test(c)?c:"h:"+c},addEvent:function(c,a,b){c.addEventListener||c.attachEvent?Event.observe($(c),HighchartsAdapter.addNS(a),b):(HighchartsAdapter._extend(c),c._highcharts_observe(a,b))},animate:function(c,a,b){var d,b=b||{};b.delay=0;b.duration=(b.duration||500)/1E3;b.afterFinish=b.complete;if(g)for(d in a)new Effect.HighchartsTransition($(c),d,a[d],b);else{if(c.attr)for(d in a)c.attr(d,a[d]);b.complete&& -b.complete()}c.attr||$(c).setStyle(a)},stop:function(c){var a;if(c._highcharts_extended&&c._highchart_animation)for(a in c._highchart_animation)c._highchart_animation[a].cancel()},each:function(c,a){$A(c).each(a)},offset:function(c){return $(c).cumulativeOffset()},fireEvent:function(c,a,b,d){c.fire?c.fire(HighchartsAdapter.addNS(a),b):c._highcharts_extended&&(b=b||{},c._highcharts_fire(a,b));b&&b.defaultPrevented&&(d=null);d&&d(b)},removeEvent:function(c,a,b){$(c).stopObserving&&(a&&(a=HighchartsAdapter.addNS(a)), -$(c).stopObserving(a,b));window===c?Event.stopObserving(c,a,b):(HighchartsAdapter._extend(c),c._highcharts_stop_observing(a,b))},washMouseEvent:function(c){return c},grep:function(c,a){return c.findAll(a)},map:function(c,a){return c.map(a)},merge:function(){function c(a,b){var d,e;for(e in b)d=b[e],a[e]=d&&typeof d==="object"&&d.constructor!==Array&&typeof d.nodeType!=="number"?c(a[e]||{},d):b[e];return a}return function(){var a=arguments,b,d={};for(b=0;b {a : 'a', b : {b1 : 'b1', b2 : 'b2_prime'}, c : 'c'} - /*merge: function(){ - function doCopy(copy, original) { - var value, - key, - undef, - nil, - same, - obj, - arr, - node; - - for (key in original) { - value = original[key]; - undef = typeof(value) === 'undefined'; - nil = value === null; - same = original === copy[key]; - - if (undef || nil || same) { - continue; - } - - obj = typeof(value) === 'object'; - arr = value && obj && value.constructor == Array; - node = !!value.nodeType; - - if (obj && !arr && !node) { - copy[key] = doCopy(typeof copy[key] == 'object' ? copy[key] : {}, value); - } - else { - copy[key] = original[key]; - } - } - return copy; - } - - var args = arguments, retVal = {}; - - for (var i = 0; i < args.length; i++) { - retVal = doCopy(retVal, args[i]); - } - - return retVal; - },*/ - merge: function () { // the built-in prototype merge function doesn't do deep copy - function doCopy(copy, original) { - var value, key; - - for (key in original) { - value = original[key]; - if (value && typeof value === 'object' && value.constructor !== Array && - typeof value.nodeType !== 'number') { - copy[key] = doCopy(copy[key] || {}, value); // copy - - } else { - copy[key] = original[key]; - } - } - return copy; - } - - function merge() { - var args = arguments, - i, - retVal = {}; - - for (i = 0; i < args.length; i++) { - retVal = doCopy(retVal, args[i]); - - } - return retVal; - } - - return merge.apply(this, arguments); - }, - // extend an object to handle highchart events (highchart objects, not svg elements). // this is a very simple way of handling events but whatever, it works (i think) _extend: function (object) { @@ -356,6 +289,7 @@ } }, _highcharts_fire: function (name, args) { + var target = this; (this._highchart_events[name] || []).each(function (fn) { // args is never null here if (args.stopped) { @@ -366,6 +300,7 @@ args.preventDefault = function () { args.defaultPrevented = true; }; + args.target = target; // If the event handler return false, prevent the default handler from executing if (fn.bind(this)(args) === false) { diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highcharts-more.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highcharts-more.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highcharts-more.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highcharts-more.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,51 @@ +/* + Highcharts JS v3.0.2 (2013-06-05) + + (c) 2009-2013 Torstein Hønsi + + License: www.highcharts.com/license +*/ +(function(k,F){function K(a,b,c){this.init.call(this,a,b,c)}function L(a,b,c){a.call(this,b,c);if(this.chart.polar)this.closeSegment=function(a){var c=this.xAxis.center;a.push("L",c[0],c[1])},this.closedStacks=!0}function M(a,b){var c=this.chart,d=this.options.animation,e=this.group,f=this.markerGroup,g=this.xAxis.center,j=c.plotLeft,l=c.plotTop;if(c.polar){if(c.renderer.isSVG)if(d===!0&&(d={}),b){if(c={translateX:g[0]+j,translateY:g[1]+l,scaleX:0.001,scaleY:0.001},e.attr(c),f)f.attrSetters=e.attrSetters, +f.attr(c)}else c={translateX:j,translateY:l,scaleX:1,scaleY:1},e.animate(c,d),f&&f.animate(c,d),this.animate=null}else a.call(this,b)}var Q=k.arrayMin,R=k.arrayMax,r=k.each,H=k.extend,m=k.merge,S=k.map,q=k.pick,x=k.pInt,n=k.getOptions().plotOptions,h=k.seriesTypes,A=k.extendClass,N=k.splat,p=k.wrap,O=k.Axis,v=k.Tick,B=k.Series,y=h.column.prototype,u=Math,I=u.round,C=u.floor,J=u.ceil,T=u.min,U=u.max,t=function(){};H(K.prototype,{init:function(a,b,c){var d=this,e=d.defaultOptions;d.chart=b;if(b.angular)e.background= +{};d.options=a=m(e,a);(a=a.background)&&r([].concat(N(a)).reverse(),function(a){var b=a.backgroundColor,a=m(d.defaultBackgroundOptions,a);if(b)a.backgroundColor=b;a.color=a.backgroundColor;c.options.plotBands.unshift(a)})},defaultOptions:{center:["50%","50%"],size:"85%",startAngle:0},defaultBackgroundOptions:{shape:"circle",borderWidth:1,borderColor:"silver",backgroundColor:{linearGradient:{x1:0,y1:0,x2:0,y2:1},stops:[[0,"#FFF"],[1,"#DDD"]]},from:Number.MIN_VALUE,innerRadius:0,to:Number.MAX_VALUE, +outerRadius:"105%"}});var G=O.prototype,v=v.prototype,V={getOffset:t,redraw:function(){this.isDirty=!1},render:function(){this.isDirty=!1},setScale:t,setCategories:t,setTitle:t},P={isRadial:!0,defaultRadialGaugeOptions:{labels:{align:"center",x:0,y:null},minorGridLineWidth:0,minorTickInterval:"auto",minorTickLength:10,minorTickPosition:"inside",minorTickWidth:1,plotBands:[],tickLength:10,tickPosition:"inside",tickWidth:2,title:{rotation:0},zIndex:2},defaultRadialXOptions:{gridLineWidth:1,labels:{align:null, +distance:15,x:0,y:null},maxPadding:0,minPadding:0,plotBands:[],showLastLabel:!1,tickLength:0},defaultRadialYOptions:{gridLineInterpolation:"circle",labels:{align:"right",x:-3,y:-2},plotBands:[],showLastLabel:!1,title:{x:4,text:null,rotation:90}},setOptions:function(a){this.options=m(this.defaultOptions,this.defaultRadialOptions,a)},getOffset:function(){G.getOffset.call(this);this.chart.axisOffset[this.side]=0;this.center=this.pane.center=h.pie.prototype.getCenter.call(this.pane)},getLinePath:function(a, +b){var c=this.center,b=q(b,c[2]/2-this.offset);return this.chart.renderer.symbols.arc(this.left+c[0],this.top+c[1],b,b,{start:this.startAngleRad,end:this.endAngleRad,open:!0,innerR:0})},setAxisTranslation:function(){G.setAxisTranslation.call(this);if(this.center&&(this.transA=this.isCircular?(this.endAngleRad-this.startAngleRad)/(this.max-this.min||1):this.center[2]/2/(this.max-this.min||1),this.isXAxis))this.minPixelPadding=this.transA*this.minPointOffset+(this.reversed?(this.endAngleRad-this.startAngleRad)/ +4:0)},beforeSetTickPositions:function(){this.autoConnect&&(this.max+=this.categories&&1||this.pointRange||this.closestPointRange)},setAxisSize:function(){G.setAxisSize.call(this);if(this.center)this.len=this.width=this.height=this.isCircular?this.center[2]*(this.endAngleRad-this.startAngleRad)/2:this.center[2]/2},getPosition:function(a,b){if(!this.isCircular)b=this.translate(a),a=this.min;return this.postTranslate(this.translate(a),q(b,this.center[2]/2)-this.offset)},postTranslate:function(a,b){var c= +this.chart,d=this.center,a=this.startAngleRad+a;return{x:c.plotLeft+d[0]+Math.cos(a)*b,y:c.plotTop+d[1]+Math.sin(a)*b}},getPlotBandPath:function(a,b,c){var d=this.center,e=this.startAngleRad,f=d[2]/2,g=[q(c.outerRadius,"100%"),c.innerRadius,q(c.thickness,10)],j=/%$/,l,o=this.isCircular;this.options.gridLineInterpolation==="polygon"?d=this.getPlotLinePath(a).concat(this.getPlotLinePath(b,!0)):(o||(g[0]=this.translate(a),g[1]=this.translate(b)),g=S(g,function(a){j.test(a)&&(a=x(a,10)*f/100);return a}), +c.shape==="circle"||!o?(a=-Math.PI/2,b=Math.PI*1.5,l=!0):(a=e+this.translate(a),b=e+this.translate(b)),d=this.chart.renderer.symbols.arc(this.left+d[0],this.top+d[1],g[0],g[0],{start:a,end:b,innerR:q(g[1],g[0]-g[2]),open:l}));return d},getPlotLinePath:function(a,b){var c=this.center,d=this.chart,e=this.getPosition(a),f,g,j;this.isCircular?j=["M",c[0]+d.plotLeft,c[1]+d.plotTop,"L",e.x,e.y]:this.options.gridLineInterpolation==="circle"?(a=this.translate(a))&&(j=this.getLinePath(0,a)):(f=d.xAxis[0], +j=[],a=this.translate(a),c=f.tickPositions,f.autoConnect&&(c=c.concat([c[0]])),b&&(c=[].concat(c).reverse()),r(c,function(c,b){g=f.getPosition(c,a);j.push(b?"L":"M",g.x,g.y)}));return j},getTitlePosition:function(){var a=this.center,b=this.chart,c=this.options.title;return{x:b.plotLeft+a[0]+(c.x||0),y:b.plotTop+a[1]-{high:0.5,middle:0.25,low:0}[c.align]*a[2]+(c.y||0)}}};p(G,"init",function(a,b,c){var i;var d=b.angular,e=b.polar,f=c.isX,g=d&&f,j,l;l=b.options;var o=c.pane||0;if(d){if(H(this,g?V:P), +j=!f)this.defaultRadialOptions=this.defaultRadialGaugeOptions}else if(e)H(this,P),this.defaultRadialOptions=(j=f)?this.defaultRadialXOptions:m(this.defaultYAxisOptions,this.defaultRadialYOptions);a.call(this,b,c);if(!g&&(d||e)){a=this.options;if(!b.panes)b.panes=[];this.pane=(i=b.panes[o]=b.panes[o]||new K(N(l.pane)[o],b,this),o=i);o=o.options;b.inverted=!1;l.chart.zoomType=null;this.startAngleRad=b=(o.startAngle-90)*Math.PI/180;this.endAngleRad=l=(q(o.endAngle,o.startAngle+360)-90)*Math.PI/180;this.offset= +a.offset||0;if((this.isCircular=j)&&c.max===F&&l-b===2*Math.PI)this.autoConnect=!0}});p(v,"getPosition",function(a,b,c,d,e){var f=this.axis;return f.getPosition?f.getPosition(c):a.call(this,b,c,d,e)});p(v,"getLabelPosition",function(a,b,c,d,e,f,g,j,l){var o=this.axis,i=f.y,h=f.align,k=(o.translate(this.pos)+o.startAngleRad+Math.PI/2)/Math.PI*180;o.isRadial?(a=o.getPosition(this.pos,o.center[2]/2+q(f.distance,-25)),f.rotation==="auto"?d.attr({rotation:k}):i===null&&(i=x(d.styles.lineHeight)*0.9-d.getBBox().height/ +2),h===null&&(h=o.isCircular?k>20&&k<160?"left":k>200&&k<340?"right":"center":"center",d.attr({align:h})),a.x+=f.x,a.y+=i):a=a.call(this,b,c,d,e,f,g,j,l);return a});p(v,"getMarkPath",function(a,b,c,d,e,f,g){var j=this.axis;j.isRadial?(a=j.getPosition(this.pos,j.center[2]/2+d),b=["M",b,c,"L",a.x,a.y]):b=a.call(this,b,c,d,e,f,g);return b});n.arearange=m(n.area,{lineWidth:1,marker:null,threshold:null,tooltip:{pointFormat:'{series.name}: {point.low} - {point.high}
    '}, +trackByArea:!0,dataLabels:{verticalAlign:null,xLow:0,xHigh:0,yLow:0,yHigh:0}});h.arearange=k.extendClass(h.area,{type:"arearange",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"low",getSegments:function(){var a=this;r(a.points,function(b){if(!a.options.connectNulls&&(b.low===null||b.high===null))b.y=null;else if(b.low===null&&b.high!==null)b.y=b.high});B.prototype.getSegments.call(this)},translate:function(){var a=this.yAxis;h.area.prototype.translate.apply(this); +r(this.points,function(b){var c=b.low,d=b.high,e=b.plotY;d===null&&c===null?b.y=null:c===null?(b.plotLow=b.plotY=null,b.plotHigh=a.toPixels(d,!0)):d===null?(b.plotLow=e,b.plotHigh=null):(b.plotLow=e,b.plotHigh=a.toPixels(d,!0))})},getSegmentPath:function(a){var b,c=[],d=a.length,e=B.prototype.getSegmentPath,f,g;g=this.options;var j=g.step;for(b=HighchartsAdapter.grep(a,function(a){return a.plotLow!==null});d--;)f=a[d],f.plotHigh!==null&&c.push({plotX:f.plotX,plotY:f.plotHigh});a=e.call(this,b);if(j)j=== +!0&&(j="left"),g.step={left:"right",center:"center",right:"left"}[j];c=e.call(this,c);g.step=j;g=[].concat(a,c);c[0]="L";this.areaPath=this.areaPath.concat(a,c);return g},drawDataLabels:function(){var a=this.data,b=a.length,c,d=[],e=B.prototype,f=this.options.dataLabels,g,j=this.chart.inverted;if(f.enabled||this._hasPointLabels){for(c=b;c--;)g=a[c],g.y=g.high,g.plotY=g.plotHigh,d[c]=g.dataLabel,g.dataLabel=g.dataLabelUpper,g.below=!1,j?(f.align="left",f.x=f.xHigh):f.y=f.yHigh;e.drawDataLabels.apply(this, +arguments);for(c=b;c--;)g=a[c],g.dataLabelUpper=g.dataLabel,g.dataLabel=d[c],g.y=g.low,g.plotY=g.plotLow,g.below=!0,j?(f.align="right",f.x=f.xLow):f.y=f.yLow;e.drawDataLabels.apply(this,arguments)}},alignDataLabel:h.column.prototype.alignDataLabel,getSymbol:h.column.prototype.getSymbol,drawPoints:t});n.areasplinerange=m(n.arearange);h.areasplinerange=A(h.arearange,{type:"areasplinerange",getPointSpline:h.spline.prototype.getPointSpline});n.columnrange=m(n.column,n.arearange,{lineWidth:1,pointRange:null}); +h.columnrange=A(h.arearange,{type:"columnrange",translate:function(){var a=this.yAxis,b;y.translate.apply(this);r(this.points,function(c){var d=c.shapeArgs;c.plotHigh=b=a.translate(c.high,0,1,0,1);c.plotLow=c.plotY;d.y=b;d.height=c.plotY-b})},trackerGroups:["group","dataLabels"],drawGraph:t,pointAttrToOptions:y.pointAttrToOptions,drawPoints:y.drawPoints,drawTracker:y.drawTracker,animate:y.animate,getColumnMetrics:y.getColumnMetrics});n.gauge=m(n.line,{dataLabels:{enabled:!0,y:15,borderWidth:1,borderColor:"silver", +borderRadius:3,style:{fontWeight:"bold"},verticalAlign:"top",zIndex:2},dial:{},pivot:{},tooltip:{headerFormat:""},showInLegend:!1});v={type:"gauge",pointClass:k.extendClass(k.Point,{setState:function(a){this.state=a}}),angular:!0,drawGraph:t,trackerGroups:["group","dataLabels"],translate:function(){var a=this.yAxis,b=this.options,c=a.center;this.generatePoints();r(this.points,function(d){var e=m(b.dial,d.dial),f=x(q(e.radius,80))*c[2]/200,g=x(q(e.baseLength,70))*f/100,j=x(q(e.rearLength,10))*f/100, +l=e.baseWidth||3,o=e.topWidth||1,i=a.startAngleRad+a.translate(d.y,null,null,null,!0);b.wrap===!1&&(i=Math.max(a.startAngleRad,Math.min(a.endAngleRad,i)));i=i*180/Math.PI;d.shapeType="path";d.shapeArgs={d:e.path||["M",-j,-l/2,"L",g,-l/2,f,-o/2,f,o/2,g,l/2,-j,l/2,"z"],translateX:c[0],translateY:c[1],rotation:i};d.plotX=c[0];d.plotY=c[1]})},drawPoints:function(){var a=this,b=a.yAxis.center,c=a.pivot,d=a.options,e=d.pivot,f=a.chart.renderer;r(a.points,function(c){var b=c.graphic,e=c.shapeArgs,o=e.d, +i=m(d.dial,c.dial);b?(b.animate(e),e.d=o):c.graphic=f[c.shapeType](e).attr({stroke:i.borderColor||"none","stroke-width":i.borderWidth||0,fill:i.backgroundColor||"black",rotation:e.rotation}).add(a.group)});c?c.animate({translateX:b[0],translateY:b[1]}):a.pivot=f.circle(0,0,q(e.radius,5)).attr({"stroke-width":e.borderWidth||0,stroke:e.borderColor||"silver",fill:e.backgroundColor||"black"}).translate(b[0],b[1]).add(a.group)},animate:function(a){var b=this;if(!a)r(b.points,function(a){var d=a.graphic; +d&&(d.attr({rotation:b.yAxis.startAngleRad*180/Math.PI}),d.animate({rotation:a.shapeArgs.rotation},b.options.animation))}),b.animate=null},render:function(){this.group=this.plotGroup("group","series",this.visible?"visible":"hidden",this.options.zIndex,this.chart.seriesGroup);h.pie.prototype.render.call(this);this.group.clip(this.chart.clipRect)},setData:h.pie.prototype.setData,drawTracker:h.column.prototype.drawTracker};h.gauge=k.extendClass(h.line,v);n.boxplot=m(n.column,{fillColor:"#FFFFFF",lineWidth:1, +medianWidth:2,states:{hover:{brightness:-0.3}},threshold:null,tooltip:{pointFormat:'{series.name}
    Minimum: {point.low}
    Lower quartile: {point.q1}
    Median: {point.median}
    Higher quartile: {point.q3}
    Maximum: {point.high}
    '},whiskerLength:"50%",whiskerWidth:2});h.boxplot=A(h.column,{type:"boxplot",pointArrayMap:["low","q1","median","q3","high"],toYData:function(a){return[a.low,a.q1,a.median,a.q3,a.high]},pointValKey:"high", +pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth"},drawDataLabels:t,translate:function(){var a=this.yAxis,b=this.pointArrayMap;h.column.prototype.translate.apply(this);r(this.points,function(c){r(b,function(b){c[b]!==null&&(c[b+"Plot"]=a.translate(c[b],0,1,0,1))})})},drawPoints:function(){var a=this,b=a.points,c=a.options,d=a.chart.renderer,e,f,g,j,l,o,i,h,k,n,s,E,p,w,m,u,y,t,x,v,B,A,z=a.doQuartiles!==!1,D=parseInt(a.options.whiskerLength,10)/100;r(b,function(b){k=b.graphic; +B=b.shapeArgs;s={};w={};u={};A=b.color||a.color;if(b.plotY!==F)if(e=b.pointAttr[b.selected?"selected":""],y=B.width,t=C(B.x),x=t+y,v=I(y/2),f=C(z?b.q1Plot:b.lowPlot),g=C(z?b.q3Plot:b.lowPlot),j=C(b.highPlot),l=C(b.lowPlot),s.stroke=b.stemColor||c.stemColor||A,s["stroke-width"]=q(b.stemWidth,c.stemWidth,c.lineWidth),s.dashstyle=b.stemDashStyle||c.stemDashStyle,w.stroke=b.whiskerColor||c.whiskerColor||A,w["stroke-width"]=q(b.whiskerWidth,c.whiskerWidth,c.lineWidth),u.stroke=b.medianColor||c.medianColor|| +A,u["stroke-width"]=q(b.medianWidth,c.medianWidth,c.lineWidth),i=s["stroke-width"]%2/2,h=t+v+i,n=["M",h,g,"L",h,j,"M",h,f,"L",h,l,"z"],z&&(i=e["stroke-width"]%2/2,h=C(h)+i,f=C(f)+i,g=C(g)+i,t+=i,x+=i,E=["M",t,g,"L",t,f,"L",x,f,"L",x,g,"L",t,g,"z"]),D&&(i=w["stroke-width"]%2/2,j+=i,l+=i,p=["M",h-v*D,j,"L",h+v*D,j,"M",h-v*D,l,"L",h+v*D,l]),i=u["stroke-width"]%2/2,o=I(b.medianPlot)+i,m=["M",t,o,"L",x,o,"z"],k)b.stem.animate({d:n}),D&&b.whiskers.animate({d:p}),z&&b.box.animate({d:E}),b.medianShape.animate({d:m}); +else{b.graphic=k=d.g().add(a.group);b.stem=d.path(n).attr(s).add(k);if(D)b.whiskers=d.path(p).attr(w).add(k);if(z)b.box=d.path(E).attr(e).add(k);b.medianShape=d.path(m).attr(u).add(k)}})}});n.errorbar=m(n.boxplot,{color:"#000000",grouping:!1,linkedTo:":previous",tooltip:{pointFormat:n.arearange.tooltip.pointFormat},whiskerWidth:null});h.errorbar=A(h.boxplot,{type:"errorbar",pointArrayMap:["low","high"],toYData:function(a){return[a.low,a.high]},pointValKey:"high",doQuartiles:!1,getColumnMetrics:function(){return this.linkedParent&& +this.linkedParent.columnMetrics||h.column.prototype.getColumnMetrics.call(this)}});p(G,"getSeriesExtremes",function(a,b){a.call(this,b);if(!this.isXAxis){var c=this,d=[],e=!0;r(c.series,function(a){if(a.visible&&a.stackKey&&!(a.type!=="waterfall"||HighchartsAdapter.inArray(a.stackKey)!==-1)){if(e)c.dataMin=c.dataMax=null,e=!1;var b=a.processedYData,j=b.length,l=b[0],h=b[0],i=a.options.threshold,k=c.stacks,n=a.stackKey,p="-"+n,s,E,m,w;for(w=0;wi)s+=E,k[m][w].setTotal(s), +k[m][w]._cum=null;sh&&(h=s);E=s}a.dataMin=l;a.dataMax=h;c.dataMin=T(q(c.dataMin,l),l,i);c.dataMax=U(q(c.dataMax,h),h,i);d.push(a.stackKey);if(typeof i==="number")if(c.dataMin>=i)c.dataMin=i,c.ignoreMinPadding=!0;else if(c.dataMax0&&!a.color)a.pointAttr=d,a.color=c})},getGraphPath:function(){var a=this.data,b=a.length,c=I(this.options.lineWidth+this.options.borderWidth)%2/2,d=[],e,f,g;for(g=1;g=d?a.shapeArgs.y+a.shapeArgs.height:a.shapeArgs.y;d=b.y>=d?b.shapeArgs.y:b.shapeArgs.y+b.shapeArgs.height; +d>c&&(e=c,c=d,d=e);return[c,d]},drawGraph:B.prototype.drawGraph});n.bubble=m(n.scatter,{dataLabels:{inside:!0,style:{color:"white",textShadow:"0px 0px 3px black"},verticalAlign:"middle"},marker:{lineColor:null,lineWidth:1},minSize:8,maxSize:"20%",tooltip:{pointFormat:"({point.x}, {point.y}), Size: {point.z}"},zThreshold:0});h.bubble=A(h.scatter,{type:"bubble",pointArrayMap:["y","z"],trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"lineColor","stroke-width":"lineWidth",fill:"fillColor"}, +applyOpacity:function(a){var b=this.options.marker,c=q(b.fillOpacity,0.5),a=a||b.fillColor||this.color;c!==1&&(a=k.Color(a).setOpacity(c).get("rgba"));return a},convertAttribs:function(){var a=B.prototype.convertAttribs.apply(this,arguments);a.fill=this.applyOpacity(a.fill);return a},getRadii:function(a,b,c,d){var e,f,g,j=this.zData,h=[];for(f=0,e=j.length;f0?(j[f]-a)/(b-a):0.5,h.push(u.ceil(c+g*(d-c))/2);this.radii=h},animate:function(a){var b=this.options.animation;if(!a)r(this.points, +function(a){var d=a.graphic,a=a.shapeArgs;d&&a&&(d.attr("r",1),d.animate({r:a.r},b))}),this.animate=null},translate:function(){var a,b=this.data,c,d,e=this.radii;h.scatter.prototype.translate.call(this);for(a=b.length;a--;)c=b[a],d=e?e[a]:0,c.negative=c.z<(this.options.zThreshold||0),d>=this.minPxSize/2?(c.shapeType="circle",c.shapeArgs={x:c.plotX,y:c.plotY,r:d},c.dlBox={x:c.plotX-d,y:c.plotY-d,width:2*d,height:2*d}):c.shapeArgs=c.plotY=c.dlBox=F},drawLegendSymbol:function(a,b){var c=x(a.itemStyle.fontSize)/ +2;b.legendSymbol=this.chart.renderer.circle(c,a.baseline-c,c).attr({zIndex:3}).add(b.legendGroup)},drawPoints:h.column.prototype.drawPoints,alignDataLabel:h.column.prototype.alignDataLabel});O.prototype.beforePadding=function(){var a=this,b=this.len,c=this.chart,d=0,e=b,f=this.isXAxis,g=f?"xData":"yData",j=this.min,h={},k=u.min(c.plotWidth,c.plotHeight),i=Number.MAX_VALUE,m=-Number.MAX_VALUE,n=this.max-j,p=b/n,s=[];this.tickPositions&&(r(this.series,function(b){var c=b.options;if(b.type==="bubble"&& +b.visible&&(a.allowZoomOutside=!0,s.push(b),f))r(["minSize","maxSize"],function(a){var b=c[a],d=/%$/.test(b),b=x(b);h[a]=d?k*b/100:b}),b.minPxSize=h.minSize,b=b.zData,b.length&&(i=u.min(i,u.max(Q(b),c.displayNegative===!1?c.zThreshold:-Number.MAX_VALUE)),m=u.max(m,R(b)))}),r(s,function(a){var b=a[g],c=b.length,k;f&&a.getRadii(i,m,h.minSize,h.maxSize);if(n>0)for(;c--;)k=a.radii[c],d=Math.min((b[c]-j)*p-k,d),e=Math.max((b[c]-j)*p+k,e)}),n>0&&q(this.options.min,this.userMin)===F&&q(this.options.max, +this.userMax)===F&&(e-=b,p*=(b+d-e)/b,this.min+=d/p,this.max+=e/p))};var z=B.prototype,n=k.Pointer.prototype;z.toXY=function(a){var b,c=this.chart;b=a.plotX;var d=a.plotY;a.rectPlotX=b;a.rectPlotY=d;a.clientX=b/Math.PI*180;b=this.xAxis.postTranslate(a.plotX,this.yAxis.len-d);a.plotX=a.polarPlotX=b.x-c.plotLeft;a.plotY=a.polarPlotY=b.y-c.plotTop};p(h.area.prototype,"init",L);p(h.areaspline.prototype,"init",L);p(h.spline.prototype,"getPointSpline",function(a,b,c,d){var e,f,g,j,h,k,i;if(this.chart.polar){e= +c.plotX;f=c.plotY;a=b[d-1];g=b[d+1];this.connectEnds&&(a||(a=b[b.length-2]),g||(g=b[1]));if(a&&g)j=a.plotX,h=a.plotY,b=g.plotX,k=g.plotY,j=(1.5*e+j)/2.5,h=(1.5*f+h)/2.5,g=(1.5*e+b)/2.5,i=(1.5*f+k)/2.5,b=Math.sqrt(Math.pow(j-e,2)+Math.pow(h-f,2)),k=Math.sqrt(Math.pow(g-e,2)+Math.pow(i-f,2)),j=Math.atan2(h-f,j-e),h=Math.atan2(i-f,g-e),i=Math.PI/2+(j+h)/2,Math.abs(j-i)>Math.PI/2&&(i-=Math.PI),j=e+Math.cos(i)*b,h=f+Math.sin(i)*b,g=e+Math.cos(Math.PI+i)*k,i=f+Math.sin(Math.PI+i)*k,c.rightContX=g,c.rightContY= +i;d?(c=["C",a.rightContX||a.plotX,a.rightContY||a.plotY,j||e,h||f,e,f],a.rightContX=a.rightContY=null):c=["M",e,f]}else c=a.call(this,b,c,d);return c});p(z,"translate",function(a){a.call(this);if(this.chart.polar&&!this.preventPostTranslate)for(var a=this.points,b=a.length;b--;)this.toXY(a[b])});p(z,"getSegmentPath",function(a,b){var c=this.points;if(this.chart.polar&&this.options.connectEnds!==!1&&b[b.length-1]===c[c.length-1]&&c[0].y!==null)this.connectEnds=!0,b=[].concat(b,[c[0]]);return a.call(this, +b)});p(z,"animate",M);p(y,"animate",M);p(z,"setTooltipPoints",function(a,b){this.chart.polar&&H(this.xAxis,{tooltipLen:360});return a.call(this,b)});p(y,"translate",function(a){var b=this.xAxis,c=this.yAxis.len,d=b.center,e=b.startAngleRad,f=this.chart.renderer,g,h;this.preventPostTranslate=!0;a.call(this);if(b.isRadial){b=this.points;for(h=b.length;h--;)g=b[h],a=g.barX+e,g.shapeType="path",g.shapeArgs={d:f.symbols.arc(d[0],d[1],c-g.plotY,null,{start:a,end:a+g.pointWidth,innerR:c-q(g.yBottom,c)})}, +this.toXY(g)}});p(y,"alignDataLabel",function(a,b,c,d,e,f){if(this.chart.polar){a=b.rectPlotX/Math.PI*180;if(d.align===null)d.align=a>20&&a<160?"left":a>200&&a<340?"right":"center";if(d.verticalAlign===null)d.verticalAlign=a<45||a>315?"bottom":a>135&&a<225?"top":"middle";z.alignDataLabel.call(this,b,c,d,e,f)}else a.call(this,b,c,d,e,f)});p(n,"getIndex",function(a,b){var c,d=this.chart,e;d.polar?(e=d.xAxis[0].center,c=b.chartX-e[0]-d.plotLeft,d=b.chartY-e[1]-d.plotTop,c=180-Math.round(Math.atan2(c, +d)/Math.PI*180)):c=a.call(this,b);return c});p(n,"getCoordinates",function(a,b){var c=this.chart,d={xAxis:[],yAxis:[]};c.polar?r(c.axes,function(a){var f=a.isXAxis,g=a.center,h=b.chartX-g[0]-c.plotLeft,g=b.chartY-g[1]-c.plotTop;d[f?"xAxis":"yAxis"].push({axis:a,value:a.translate(f?Math.PI-Math.atan2(h,g):Math.sqrt(Math.pow(h,2)+Math.pow(g,2)),!0)})}):d=a.call(this,b);return d})})(Highcharts); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highcharts-more.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highcharts-more.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highcharts-more.src.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highcharts-more.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,2525 @@ +// ==ClosureCompiler== +// @compilation_level SIMPLE_OPTIMIZATIONS + +/** + * @license Highcharts JS v3.0.2 (2013-06-05) + * + * (c) 2009-2013 Torstein Hønsi + * + * License: www.highcharts.com/license + */ + +// JSLint options: +/*global Highcharts, HighchartsAdapter, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */ + +(function (Highcharts, UNDEFINED) { +var arrayMin = Highcharts.arrayMin, + arrayMax = Highcharts.arrayMax, + each = Highcharts.each, + extend = Highcharts.extend, + merge = Highcharts.merge, + map = Highcharts.map, + pick = Highcharts.pick, + pInt = Highcharts.pInt, + defaultPlotOptions = Highcharts.getOptions().plotOptions, + seriesTypes = Highcharts.seriesTypes, + extendClass = Highcharts.extendClass, + splat = Highcharts.splat, + wrap = Highcharts.wrap, + Axis = Highcharts.Axis, + Tick = Highcharts.Tick, + Series = Highcharts.Series, + colProto = seriesTypes.column.prototype, + math = Math, + mathRound = math.round, + mathFloor = math.floor, + mathCeil = math.ceil, + mathMin = math.min, + mathMax = math.max, + noop = function () {};/** + * The Pane object allows options that are common to a set of X and Y axes. + * + * In the future, this can be extended to basic Highcharts and Highstock. + */ +function Pane(options, chart, firstAxis) { + this.init.call(this, options, chart, firstAxis); +} + +// Extend the Pane prototype +extend(Pane.prototype, { + + /** + * Initiate the Pane object + */ + init: function (options, chart, firstAxis) { + var pane = this, + backgroundOption, + defaultOptions = pane.defaultOptions; + + pane.chart = chart; + + // Set options + if (chart.angular) { // gauges + defaultOptions.background = {}; // gets extended by this.defaultBackgroundOptions + } + pane.options = options = merge(defaultOptions, options); + + backgroundOption = options.background; + + // To avoid having weighty logic to place, update and remove the backgrounds, + // push them to the first axis' plot bands and borrow the existing logic there. + if (backgroundOption) { + each([].concat(splat(backgroundOption)).reverse(), function (config) { + var backgroundColor = config.backgroundColor; // if defined, replace the old one (specific for gradients) + config = merge(pane.defaultBackgroundOptions, config); + if (backgroundColor) { + config.backgroundColor = backgroundColor; + } + config.color = config.backgroundColor; // due to naming in plotBands + firstAxis.options.plotBands.unshift(config); + }); + } + }, + + /** + * The default options object + */ + defaultOptions: { + // background: {conditional}, + center: ['50%', '50%'], + size: '85%', + startAngle: 0 + //endAngle: startAngle + 360 + }, + + /** + * The default background options + */ + defaultBackgroundOptions: { + shape: 'circle', + borderWidth: 1, + borderColor: 'silver', + backgroundColor: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0, '#FFF'], + [1, '#DDD'] + ] + }, + from: Number.MIN_VALUE, // corrected to axis min + innerRadius: 0, + to: Number.MAX_VALUE, // corrected to axis max + outerRadius: '105%' + } + +}); +var axisProto = Axis.prototype, + tickProto = Tick.prototype; + +/** + * Augmented methods for the x axis in order to hide it completely, used for the X axis in gauges + */ +var hiddenAxisMixin = { + getOffset: noop, + redraw: function () { + this.isDirty = false; // prevent setting Y axis dirty + }, + render: function () { + this.isDirty = false; // prevent setting Y axis dirty + }, + setScale: noop, + setCategories: noop, + setTitle: noop +}; + +/** + * Augmented methods for the value axis + */ +/*jslint unparam: true*/ +var radialAxisMixin = { + isRadial: true, + + /** + * The default options extend defaultYAxisOptions + */ + defaultRadialGaugeOptions: { + labels: { + align: 'center', + x: 0, + y: null // auto + }, + minorGridLineWidth: 0, + minorTickInterval: 'auto', + minorTickLength: 10, + minorTickPosition: 'inside', + minorTickWidth: 1, + plotBands: [], + tickLength: 10, + tickPosition: 'inside', + tickWidth: 2, + title: { + rotation: 0 + }, + zIndex: 2 // behind dials, points in the series group + }, + + // Circular axis around the perimeter of a polar chart + defaultRadialXOptions: { + gridLineWidth: 1, // spokes + labels: { + align: null, // auto + distance: 15, + x: 0, + y: null // auto + }, + maxPadding: 0, + minPadding: 0, + plotBands: [], + showLastLabel: false, + tickLength: 0 + }, + + // Radial axis, like a spoke in a polar chart + defaultRadialYOptions: { + gridLineInterpolation: 'circle', + labels: { + align: 'right', + x: -3, + y: -2 + }, + plotBands: [], + showLastLabel: false, + title: { + x: 4, + text: null, + rotation: 90 + } + }, + + /** + * Merge and set options + */ + setOptions: function (userOptions) { + + this.options = merge( + this.defaultOptions, + this.defaultRadialOptions, + userOptions + ); + + }, + + /** + * Wrap the getOffset method to return zero offset for title or labels in a radial + * axis + */ + getOffset: function () { + // Call the Axis prototype method (the method we're in now is on the instance) + axisProto.getOffset.call(this); + + // Title or label offsets are not counted + this.chart.axisOffset[this.side] = 0; + + // Set the center array + this.center = this.pane.center = seriesTypes.pie.prototype.getCenter.call(this.pane); + }, + + + /** + * Get the path for the axis line. This method is also referenced in the getPlotLinePath + * method. + */ + getLinePath: function (lineWidth, radius) { + var center = this.center; + radius = pick(radius, center[2] / 2 - this.offset); + + return this.chart.renderer.symbols.arc( + this.left + center[0], + this.top + center[1], + radius, + radius, + { + start: this.startAngleRad, + end: this.endAngleRad, + open: true, + innerR: 0 + } + ); + }, + + /** + * Override setAxisTranslation by setting the translation to the difference + * in rotation. This allows the translate method to return angle for + * any given value. + */ + setAxisTranslation: function () { + + // Call uber method + axisProto.setAxisTranslation.call(this); + + // Set transA and minPixelPadding + if (this.center) { // it's not defined the first time + if (this.isCircular) { + + this.transA = (this.endAngleRad - this.startAngleRad) / + ((this.max - this.min) || 1); + + + } else { + this.transA = (this.center[2] / 2) / ((this.max - this.min) || 1); + } + + if (this.isXAxis) { + this.minPixelPadding = this.transA * this.minPointOffset + + (this.reversed ? (this.endAngleRad - this.startAngleRad) / 4 : 0); // ??? + } + } + }, + + /** + * In case of auto connect, add one closestPointRange to the max value right before + * tickPositions are computed, so that ticks will extend passed the real max. + */ + beforeSetTickPositions: function () { + if (this.autoConnect) { + this.max += (this.categories && 1) || this.pointRange || this.closestPointRange; // #1197 + } + }, + + /** + * Override the setAxisSize method to use the arc's circumference as length. This + * allows tickPixelInterval to apply to pixel lengths along the perimeter + */ + setAxisSize: function () { + + axisProto.setAxisSize.call(this); + + if (this.center) { // it's not defined the first time + this.len = this.width = this.height = this.isCircular ? + this.center[2] * (this.endAngleRad - this.startAngleRad) / 2 : + this.center[2] / 2; + } + }, + + /** + * Returns the x, y coordinate of a point given by a value and a pixel distance + * from center + */ + getPosition: function (value, length) { + if (!this.isCircular) { + length = this.translate(value); + value = this.min; + } + + return this.postTranslate( + this.translate(value), + pick(length, this.center[2] / 2) - this.offset + ); + }, + + /** + * Translate from intermediate plotX (angle), plotY (axis.len - radius) to final chart coordinates. + */ + postTranslate: function (angle, radius) { + + var chart = this.chart, + center = this.center; + + angle = this.startAngleRad + angle; + + return { + x: chart.plotLeft + center[0] + Math.cos(angle) * radius, + y: chart.plotTop + center[1] + Math.sin(angle) * radius + }; + + }, + + /** + * Find the path for plot bands along the radial axis + */ + getPlotBandPath: function (from, to, options) { + var center = this.center, + startAngleRad = this.startAngleRad, + fullRadius = center[2] / 2, + radii = [ + pick(options.outerRadius, '100%'), + options.innerRadius, + pick(options.thickness, 10) + ], + percentRegex = /%$/, + start, + end, + open, + isCircular = this.isCircular, // X axis in a polar chart + ret; + + // Polygonal plot bands + if (this.options.gridLineInterpolation === 'polygon') { + ret = this.getPlotLinePath(from).concat(this.getPlotLinePath(to, true)); + + // Circular grid bands + } else { + + // Plot bands on Y axis (radial axis) - inner and outer radius depend on to and from + if (!isCircular) { + radii[0] = this.translate(from); + radii[1] = this.translate(to); + } + + // Convert percentages to pixel values + radii = map(radii, function (radius) { + if (percentRegex.test(radius)) { + radius = (pInt(radius, 10) * fullRadius) / 100; + } + return radius; + }); + + // Handle full circle + if (options.shape === 'circle' || !isCircular) { + start = -Math.PI / 2; + end = Math.PI * 1.5; + open = true; + } else { + start = startAngleRad + this.translate(from); + end = startAngleRad + this.translate(to); + } + + + ret = this.chart.renderer.symbols.arc( + this.left + center[0], + this.top + center[1], + radii[0], + radii[0], + { + start: start, + end: end, + innerR: pick(radii[1], radii[0] - radii[2]), + open: open + } + ); + } + + return ret; + }, + + /** + * Find the path for plot lines perpendicular to the radial axis. + */ + getPlotLinePath: function (value, reverse) { + var axis = this, + center = axis.center, + chart = axis.chart, + end = axis.getPosition(value), + xAxis, + xy, + tickPositions, + ret; + + // Spokes + if (axis.isCircular) { + ret = ['M', center[0] + chart.plotLeft, center[1] + chart.plotTop, 'L', end.x, end.y]; + + // Concentric circles + } else if (axis.options.gridLineInterpolation === 'circle') { + value = axis.translate(value); + if (value) { // a value of 0 is in the center + ret = axis.getLinePath(0, value); + } + // Concentric polygons + } else { + xAxis = chart.xAxis[0]; + ret = []; + value = axis.translate(value); + tickPositions = xAxis.tickPositions; + if (xAxis.autoConnect) { + tickPositions = tickPositions.concat([tickPositions[0]]); + } + // Reverse the positions for concatenation of polygonal plot bands + if (reverse) { + tickPositions = [].concat(tickPositions).reverse(); + } + + each(tickPositions, function (pos, i) { + xy = xAxis.getPosition(pos, value); + ret.push(i ? 'L' : 'M', xy.x, xy.y); + }); + + } + return ret; + }, + + /** + * Find the position for the axis title, by default inside the gauge + */ + getTitlePosition: function () { + var center = this.center, + chart = this.chart, + titleOptions = this.options.title; + + return { + x: chart.plotLeft + center[0] + (titleOptions.x || 0), + y: chart.plotTop + center[1] - ({ high: 0.5, middle: 0.25, low: 0 }[titleOptions.align] * + center[2]) + (titleOptions.y || 0) + }; + } + +}; +/*jslint unparam: false*/ + +/** + * Override axisProto.init to mix in special axis instance functions and function overrides + */ +wrap(axisProto, 'init', function (proceed, chart, userOptions) { + var axis = this, + angular = chart.angular, + polar = chart.polar, + isX = userOptions.isX, + isHidden = angular && isX, + isCircular, + startAngleRad, + endAngleRad, + options, + chartOptions = chart.options, + paneIndex = userOptions.pane || 0, + pane, + paneOptions; + + // Before prototype.init + if (angular) { + extend(this, isHidden ? hiddenAxisMixin : radialAxisMixin); + isCircular = !isX; + if (isCircular) { + this.defaultRadialOptions = this.defaultRadialGaugeOptions; + } + + } else if (polar) { + //extend(this, userOptions.isX ? radialAxisMixin : radialAxisMixin); + extend(this, radialAxisMixin); + isCircular = isX; + this.defaultRadialOptions = isX ? this.defaultRadialXOptions : merge(this.defaultYAxisOptions, this.defaultRadialYOptions); + + } + + // Run prototype.init + proceed.call(this, chart, userOptions); + + if (!isHidden && (angular || polar)) { + options = this.options; + + // Create the pane and set the pane options. + if (!chart.panes) { + chart.panes = []; + } + this.pane = pane = chart.panes[paneIndex] = chart.panes[paneIndex] || new Pane( + splat(chartOptions.pane)[paneIndex], + chart, + axis + ); + paneOptions = pane.options; + + + // Disable certain features on angular and polar axes + chart.inverted = false; + chartOptions.chart.zoomType = null; + + // Start and end angle options are + // given in degrees relative to top, while internal computations are + // in radians relative to right (like SVG). + this.startAngleRad = startAngleRad = (paneOptions.startAngle - 90) * Math.PI / 180; + this.endAngleRad = endAngleRad = (pick(paneOptions.endAngle, paneOptions.startAngle + 360) - 90) * Math.PI / 180; + this.offset = options.offset || 0; + + this.isCircular = isCircular; + + // Automatically connect grid lines? + if (isCircular && userOptions.max === UNDEFINED && endAngleRad - startAngleRad === 2 * Math.PI) { + this.autoConnect = true; + } + } + +}); + +/** + * Add special cases within the Tick class' methods for radial axes. + */ +wrap(tickProto, 'getPosition', function (proceed, horiz, pos, tickmarkOffset, old) { + var axis = this.axis; + + return axis.getPosition ? + axis.getPosition(pos) : + proceed.call(this, horiz, pos, tickmarkOffset, old); +}); + +/** + * Wrap the getLabelPosition function to find the center position of the label + * based on the distance option + */ +wrap(tickProto, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index, step) { + var axis = this.axis, + optionsY = labelOptions.y, + ret, + align = labelOptions.align, + angle = (axis.translate(this.pos) + axis.startAngleRad + Math.PI / 2) / Math.PI * 180; + + if (axis.isRadial) { + ret = axis.getPosition(this.pos, (axis.center[2] / 2) + pick(labelOptions.distance, -25)); + + // Automatically rotated + if (labelOptions.rotation === 'auto') { + label.attr({ + rotation: angle + }); + + // Vertically centered + } else if (optionsY === null) { + optionsY = pInt(label.styles.lineHeight) * 0.9 - label.getBBox().height / 2; + + } + + // Automatic alignment + if (align === null) { + if (axis.isCircular) { + if (angle > 20 && angle < 160) { + align = 'left'; // right hemisphere + } else if (angle > 200 && angle < 340) { + align = 'right'; // left hemisphere + } else { + align = 'center'; // top or bottom + } + } else { + align = 'center'; + } + label.attr({ + align: align + }); + } + + ret.x += labelOptions.x; + ret.y += optionsY; + + } else { + ret = proceed.call(this, x, y, label, horiz, labelOptions, tickmarkOffset, index, step); + } + return ret; +}); + +/** + * Wrap the getMarkPath function to return the path of the radial marker + */ +wrap(tickProto, 'getMarkPath', function (proceed, x, y, tickLength, tickWidth, horiz, renderer) { + var axis = this.axis, + endPoint, + ret; + + if (axis.isRadial) { + endPoint = axis.getPosition(this.pos, axis.center[2] / 2 + tickLength); + ret = [ + 'M', + x, + y, + 'L', + endPoint.x, + endPoint.y + ]; + } else { + ret = proceed.call(this, x, y, tickLength, tickWidth, horiz, renderer); + } + return ret; +});/* + * The AreaRangeSeries class + * + */ + +/** + * Extend the default options with map options + */ +defaultPlotOptions.arearange = merge(defaultPlotOptions.area, { + lineWidth: 1, + marker: null, + threshold: null, + tooltip: { + pointFormat: '{series.name}: {point.low} - {point.high}
    ' + }, + trackByArea: true, + dataLabels: { + verticalAlign: null, + xLow: 0, + xHigh: 0, + yLow: 0, + yHigh: 0 + } +}); + +/** + * Add the series type + */ +seriesTypes.arearange = Highcharts.extendClass(seriesTypes.area, { + type: 'arearange', + pointArrayMap: ['low', 'high'], + toYData: function (point) { + return [point.low, point.high]; + }, + pointValKey: 'low', + + /** + * Extend getSegments to force null points if the higher value is null. #1703. + */ + getSegments: function () { + var series = this; + + each(series.points, function (point) { + if (!series.options.connectNulls && (point.low === null || point.high === null)) { + point.y = null; + } else if (point.low === null && point.high !== null) { + point.y = point.high; + } + }); + Series.prototype.getSegments.call(this); + }, + + /** + * Translate data points from raw values x and y to plotX and plotY + */ + translate: function () { + var series = this, + yAxis = series.yAxis; + + seriesTypes.area.prototype.translate.apply(series); + + // Set plotLow and plotHigh + each(series.points, function (point) { + + var low = point.low, + high = point.high, + plotY = point.plotY; + + if (high === null && low === null) { + point.y = null; + } else if (low === null) { + point.plotLow = point.plotY = null; + point.plotHigh = yAxis.toPixels(high, true); + } else if (high === null) { + point.plotLow = plotY; + point.plotHigh = null; + } else { + point.plotLow = plotY; + point.plotHigh = yAxis.toPixels(high, true); + } + }); + }, + + /** + * Extend the line series' getSegmentPath method by applying the segment + * path to both lower and higher values of the range + */ + getSegmentPath: function (segment) { + + var lowSegment, + highSegment = [], + i = segment.length, + baseGetSegmentPath = Series.prototype.getSegmentPath, + point, + linePath, + lowerPath, + options = this.options, + step = options.step, + higherPath; + + // Remove nulls from low segment + lowSegment = HighchartsAdapter.grep(segment, function (point) { + return point.plotLow !== null; + }); + + // Make a segment with plotX and plotY for the top values + while (i--) { + point = segment[i]; + if (point.plotHigh !== null) { + highSegment.push({ + plotX: point.plotX, + plotY: point.plotHigh + }); + } + } + + // Get the paths + lowerPath = baseGetSegmentPath.call(this, lowSegment); + if (step) { + if (step === true) { + step = 'left'; + } + options.step = { left: 'right', center: 'center', right: 'left' }[step]; // swap for reading in getSegmentPath + } + higherPath = baseGetSegmentPath.call(this, highSegment); + options.step = step; + + // Create a line on both top and bottom of the range + linePath = [].concat(lowerPath, higherPath); + + // For the area path, we need to change the 'move' statement into 'lineTo' or 'curveTo' + higherPath[0] = 'L'; // this probably doesn't work for spline + this.areaPath = this.areaPath.concat(lowerPath, higherPath); + + return linePath; + }, + + /** + * Extend the basic drawDataLabels method by running it for both lower and higher + * values. + */ + drawDataLabels: function () { + + var data = this.data, + length = data.length, + i, + originalDataLabels = [], + seriesProto = Series.prototype, + dataLabelOptions = this.options.dataLabels, + point, + inverted = this.chart.inverted; + + if (dataLabelOptions.enabled || this._hasPointLabels) { + + // Step 1: set preliminary values for plotY and dataLabel and draw the upper labels + i = length; + while (i--) { + point = data[i]; + + // Set preliminary values + point.y = point.high; + point.plotY = point.plotHigh; + + // Store original data labels and set preliminary label objects to be picked up + // in the uber method + originalDataLabels[i] = point.dataLabel; + point.dataLabel = point.dataLabelUpper; + + // Set the default offset + point.below = false; + if (inverted) { + dataLabelOptions.align = 'left'; + dataLabelOptions.x = dataLabelOptions.xHigh; + } else { + dataLabelOptions.y = dataLabelOptions.yHigh; + } + } + seriesProto.drawDataLabels.apply(this, arguments); // #1209 + + // Step 2: reorganize and handle data labels for the lower values + i = length; + while (i--) { + point = data[i]; + + // Move the generated labels from step 1, and reassign the original data labels + point.dataLabelUpper = point.dataLabel; + point.dataLabel = originalDataLabels[i]; + + // Reset values + point.y = point.low; + point.plotY = point.plotLow; + + // Set the default offset + point.below = true; + if (inverted) { + dataLabelOptions.align = 'right'; + dataLabelOptions.x = dataLabelOptions.xLow; + } else { + dataLabelOptions.y = dataLabelOptions.yLow; + } + } + seriesProto.drawDataLabels.apply(this, arguments); + } + + }, + + alignDataLabel: seriesTypes.column.prototype.alignDataLabel, + + getSymbol: seriesTypes.column.prototype.getSymbol, + + drawPoints: noop +});/** + * The AreaSplineRangeSeries class + */ + +defaultPlotOptions.areasplinerange = merge(defaultPlotOptions.arearange); + +/** + * AreaSplineRangeSeries object + */ +seriesTypes.areasplinerange = extendClass(seriesTypes.arearange, { + type: 'areasplinerange', + getPointSpline: seriesTypes.spline.prototype.getPointSpline +});/** + * The ColumnRangeSeries class + */ +defaultPlotOptions.columnrange = merge(defaultPlotOptions.column, defaultPlotOptions.arearange, { + lineWidth: 1, + pointRange: null +}); + +/** + * ColumnRangeSeries object + */ +seriesTypes.columnrange = extendClass(seriesTypes.arearange, { + type: 'columnrange', + /** + * Translate data points from raw values x and y to plotX and plotY + */ + translate: function () { + var series = this, + yAxis = series.yAxis, + plotHigh; + + colProto.translate.apply(series); + + // Set plotLow and plotHigh + each(series.points, function (point) { + var shapeArgs = point.shapeArgs; + + point.plotHigh = plotHigh = yAxis.translate(point.high, 0, 1, 0, 1); + point.plotLow = point.plotY; + + // adjust shape + shapeArgs.y = plotHigh; + shapeArgs.height = point.plotY - plotHigh; + + }); + }, + trackerGroups: ['group', 'dataLabels'], + drawGraph: noop, + pointAttrToOptions: colProto.pointAttrToOptions, + drawPoints: colProto.drawPoints, + drawTracker: colProto.drawTracker, + animate: colProto.animate, + getColumnMetrics: colProto.getColumnMetrics +});/* + * The GaugeSeries class + */ + + + +/** + * Extend the default options + */ +defaultPlotOptions.gauge = merge(defaultPlotOptions.line, { + dataLabels: { + enabled: true, + y: 15, + borderWidth: 1, + borderColor: 'silver', + borderRadius: 3, + style: { + fontWeight: 'bold' + }, + verticalAlign: 'top', + zIndex: 2 + }, + dial: { + // radius: '80%', + // backgroundColor: 'black', + // borderColor: 'silver', + // borderWidth: 0, + // baseWidth: 3, + // topWidth: 1, + // baseLength: '70%' // of radius + // rearLength: '10%' + }, + pivot: { + //radius: 5, + //borderWidth: 0 + //borderColor: 'silver', + //backgroundColor: 'black' + }, + tooltip: { + headerFormat: '' + }, + showInLegend: false +}); + +/** + * Extend the point object + */ +var GaugePoint = Highcharts.extendClass(Highcharts.Point, { + /** + * Don't do any hover colors or anything + */ + setState: function (state) { + this.state = state; + } +}); + + +/** + * Add the series type + */ +var GaugeSeries = { + type: 'gauge', + pointClass: GaugePoint, + + // chart.angular will be set to true when a gauge series is present, and this will + // be used on the axes + angular: true, + drawGraph: noop, + trackerGroups: ['group', 'dataLabels'], + + /** + * Calculate paths etc + */ + translate: function () { + + var series = this, + yAxis = series.yAxis, + options = series.options, + center = yAxis.center; + + series.generatePoints(); + + each(series.points, function (point) { + + var dialOptions = merge(options.dial, point.dial), + radius = (pInt(pick(dialOptions.radius, 80)) * center[2]) / 200, + baseLength = (pInt(pick(dialOptions.baseLength, 70)) * radius) / 100, + rearLength = (pInt(pick(dialOptions.rearLength, 10)) * radius) / 100, + baseWidth = dialOptions.baseWidth || 3, + topWidth = dialOptions.topWidth || 1, + rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true); + + // Handle the wrap option + if (options.wrap === false) { + rotation = Math.max(yAxis.startAngleRad, Math.min(yAxis.endAngleRad, rotation)); + } + rotation = rotation * 180 / Math.PI; + + point.shapeType = 'path'; + point.shapeArgs = { + d: dialOptions.path || [ + 'M', + -rearLength, -baseWidth / 2, + 'L', + baseLength, -baseWidth / 2, + radius, -topWidth / 2, + radius, topWidth / 2, + baseLength, baseWidth / 2, + -rearLength, baseWidth / 2, + 'z' + ], + translateX: center[0], + translateY: center[1], + rotation: rotation + }; + + // Positions for data label + point.plotX = center[0]; + point.plotY = center[1]; + }); + }, + + /** + * Draw the points where each point is one needle + */ + drawPoints: function () { + + var series = this, + center = series.yAxis.center, + pivot = series.pivot, + options = series.options, + pivotOptions = options.pivot, + renderer = series.chart.renderer; + + each(series.points, function (point) { + + var graphic = point.graphic, + shapeArgs = point.shapeArgs, + d = shapeArgs.d, + dialOptions = merge(options.dial, point.dial); // #1233 + + if (graphic) { + graphic.animate(shapeArgs); + shapeArgs.d = d; // animate alters it + } else { + point.graphic = renderer[point.shapeType](shapeArgs) + .attr({ + stroke: dialOptions.borderColor || 'none', + 'stroke-width': dialOptions.borderWidth || 0, + fill: dialOptions.backgroundColor || 'black', + rotation: shapeArgs.rotation // required by VML when animation is false + }) + .add(series.group); + } + }); + + // Add or move the pivot + if (pivot) { + pivot.animate({ // #1235 + translateX: center[0], + translateY: center[1] + }); + } else { + series.pivot = renderer.circle(0, 0, pick(pivotOptions.radius, 5)) + .attr({ + 'stroke-width': pivotOptions.borderWidth || 0, + stroke: pivotOptions.borderColor || 'silver', + fill: pivotOptions.backgroundColor || 'black' + }) + .translate(center[0], center[1]) + .add(series.group); + } + }, + + /** + * Animate the arrow up from startAngle + */ + animate: function (init) { + var series = this; + + if (!init) { + each(series.points, function (point) { + var graphic = point.graphic; + + if (graphic) { + // start value + graphic.attr({ + rotation: series.yAxis.startAngleRad * 180 / Math.PI + }); + + // animate + graphic.animate({ + rotation: point.shapeArgs.rotation + }, series.options.animation); + } + }); + + // delete this function to allow it only once + series.animate = null; + } + }, + + render: function () { + this.group = this.plotGroup( + 'group', + 'series', + this.visible ? 'visible' : 'hidden', + this.options.zIndex, + this.chart.seriesGroup + ); + seriesTypes.pie.prototype.render.call(this); + this.group.clip(this.chart.clipRect); + }, + + setData: seriesTypes.pie.prototype.setData, + drawTracker: seriesTypes.column.prototype.drawTracker +}; +seriesTypes.gauge = Highcharts.extendClass(seriesTypes.line, GaugeSeries);/* **************************************************************************** + * Start Box plot series code * + *****************************************************************************/ + +// Set default options +defaultPlotOptions.boxplot = merge(defaultPlotOptions.column, { + fillColor: '#FFFFFF', + lineWidth: 1, + //medianColor: null, + medianWidth: 2, + states: { + hover: { + brightness: -0.3 + } + }, + //stemColor: null, + //stemDashStyle: 'solid' + //stemWidth: null, + threshold: null, + tooltip: { + pointFormat: '{series.name}
    ' + + 'Minimum: {point.low}
    ' + + 'Lower quartile: {point.q1}
    ' + + 'Median: {point.median}
    ' + + 'Higher quartile: {point.q3}
    ' + + 'Maximum: {point.high}
    ' + }, + //whiskerColor: null, + whiskerLength: '50%', + whiskerWidth: 2 +}); + +// Create the series object +seriesTypes.boxplot = extendClass(seriesTypes.column, { + type: 'boxplot', + pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // array point configs are mapped to this + toYData: function (point) { // return a plain array for speedy calculation + return [point.low, point.q1, point.median, point.q3, point.high]; + }, + pointValKey: 'high', // defines the top of the tracker + + /** + * One-to-one mapping from options to SVG attributes + */ + pointAttrToOptions: { // mapping between SVG attributes and the corresponding options + fill: 'fillColor', + stroke: 'color', + 'stroke-width': 'lineWidth' + }, + + /** + * Disable data labels for box plot + */ + drawDataLabels: noop, + + /** + * Translate data points from raw values x and y to plotX and plotY + */ + translate: function () { + var series = this, + yAxis = series.yAxis, + pointArrayMap = series.pointArrayMap; + + seriesTypes.column.prototype.translate.apply(series); + + // do the translation on each point dimension + each(series.points, function (point) { + each(pointArrayMap, function (key) { + if (point[key] !== null) { + point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1); + } + }); + }); + }, + + /** + * Draw the data points + */ + drawPoints: function () { + var series = this, //state = series.state, + points = series.points, + options = series.options, + chart = series.chart, + renderer = chart.renderer, + pointAttr, + q1Plot, + q3Plot, + highPlot, + lowPlot, + medianPlot, + crispCorr, + crispX, + graphic, + stemPath, + stemAttr, + boxPath, + whiskersPath, + whiskersAttr, + medianPath, + medianAttr, + width, + left, + right, + halfWidth, + shapeArgs, + color, + doQuartiles = series.doQuartiles !== false, // error bar inherits this series type but doesn't do quartiles + whiskerLength = parseInt(series.options.whiskerLength, 10) / 100; + + + each(points, function (point) { + + graphic = point.graphic; + shapeArgs = point.shapeArgs; // the box + stemAttr = {}; + whiskersAttr = {}; + medianAttr = {}; + color = point.color || series.color; + + if (point.plotY !== UNDEFINED) { + + pointAttr = point.pointAttr[point.selected ? 'selected' : '']; + + // crisp vector coordinates + width = shapeArgs.width; + left = mathFloor(shapeArgs.x); + right = left + width; + halfWidth = mathRound(width / 2); + //crispX = mathRound(left + halfWidth) + crispCorr; + q1Plot = mathFloor(doQuartiles ? point.q1Plot : point.lowPlot);// + crispCorr; + q3Plot = mathFloor(doQuartiles ? point.q3Plot : point.lowPlot);// + crispCorr; + highPlot = mathFloor(point.highPlot);// + crispCorr; + lowPlot = mathFloor(point.lowPlot);// + crispCorr; + + // Stem attributes + stemAttr.stroke = point.stemColor || options.stemColor || color; + stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth); + stemAttr.dashstyle = point.stemDashStyle || options.stemDashStyle; + + // Whiskers attributes + whiskersAttr.stroke = point.whiskerColor || options.whiskerColor || color; + whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth); + + // Median attributes + medianAttr.stroke = point.medianColor || options.medianColor || color; + medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth); + + + // The stem + crispCorr = (stemAttr['stroke-width'] % 2) / 2; + crispX = left + halfWidth + crispCorr; + stemPath = [ + // stem up + 'M', + crispX, q3Plot, + 'L', + crispX, highPlot, + + // stem down + 'M', + crispX, q1Plot, + 'L', + crispX, lowPlot, + 'z' + ]; + + // The box + if (doQuartiles) { + crispCorr = (pointAttr['stroke-width'] % 2) / 2; + crispX = mathFloor(crispX) + crispCorr; + q1Plot = mathFloor(q1Plot) + crispCorr; + q3Plot = mathFloor(q3Plot) + crispCorr; + left += crispCorr; + right += crispCorr; + boxPath = [ + 'M', + left, q3Plot, + 'L', + left, q1Plot, + 'L', + right, q1Plot, + 'L', + right, q3Plot, + 'L', + left, q3Plot, + 'z' + ]; + } + + // The whiskers + if (whiskerLength) { + crispCorr = (whiskersAttr['stroke-width'] % 2) / 2; + highPlot = highPlot + crispCorr; + lowPlot = lowPlot + crispCorr; + whiskersPath = [ + // High whisker + 'M', + crispX - halfWidth * whiskerLength, + highPlot, + 'L', + crispX + halfWidth * whiskerLength, + highPlot, + + // Low whisker + 'M', + crispX - halfWidth * whiskerLength, + lowPlot, + 'L', + crispX + halfWidth * whiskerLength, + lowPlot + ]; + } + + // The median + crispCorr = (medianAttr['stroke-width'] % 2) / 2; + medianPlot = mathRound(point.medianPlot) + crispCorr; + medianPath = [ + 'M', + left, + medianPlot, + 'L', + right, + medianPlot, + 'z' + ]; + + // Create or update the graphics + if (graphic) { // update + + point.stem.animate({ d: stemPath }); + if (whiskerLength) { + point.whiskers.animate({ d: whiskersPath }); + } + if (doQuartiles) { + point.box.animate({ d: boxPath }); + } + point.medianShape.animate({ d: medianPath }); + + } else { // create new + point.graphic = graphic = renderer.g() + .add(series.group); + + point.stem = renderer.path(stemPath) + .attr(stemAttr) + .add(graphic); + + if (whiskerLength) { + point.whiskers = renderer.path(whiskersPath) + .attr(whiskersAttr) + .add(graphic); + } + if (doQuartiles) { + point.box = renderer.path(boxPath) + .attr(pointAttr) + .add(graphic); + } + point.medianShape = renderer.path(medianPath) + .attr(medianAttr) + .add(graphic); + } + } + }); + + } + + +}); + +/* **************************************************************************** + * End Box plot series code * + *****************************************************************************/ +/* **************************************************************************** + * Start error bar series code * + *****************************************************************************/ + +// 1 - set default options +defaultPlotOptions.errorbar = merge(defaultPlotOptions.boxplot, { + color: '#000000', + grouping: false, + linkedTo: ':previous', + tooltip: { + pointFormat: defaultPlotOptions.arearange.tooltip.pointFormat + }, + whiskerWidth: null +}); + +// 2 - Create the series object +seriesTypes.errorbar = extendClass(seriesTypes.boxplot, { + type: 'errorbar', + pointArrayMap: ['low', 'high'], // array point configs are mapped to this + toYData: function (point) { // return a plain array for speedy calculation + return [point.low, point.high]; + }, + pointValKey: 'high', // defines the top of the tracker + doQuartiles: false, + + /** + * Get the width and X offset, either on top of the linked series column + * or standalone + */ + getColumnMetrics: function () { + return (this.linkedParent && this.linkedParent.columnMetrics) || + seriesTypes.column.prototype.getColumnMetrics.call(this); + } +}); + +/* **************************************************************************** + * End error bar series code * + *****************************************************************************/ +/* **************************************************************************** + * Start Waterfall series code * + *****************************************************************************/ + +wrap(axisProto, 'getSeriesExtremes', function (proceed, renew) { + // Run uber method + proceed.call(this, renew); + + if (this.isXAxis) { + return; + } + + var axis = this, + visitedStacks = [], + resetMinMax = true; + + + // recalculate extremes for each waterfall stack + each(axis.series, function (series) { + // process only visible, waterfall series, one from each stack + if (!series.visible || !series.stackKey || series.type !== 'waterfall' || HighchartsAdapter.inArray(series.stackKey) !== -1) { + return; + } + + // reset previously found dataMin and dataMax, do it only once + if (resetMinMax) { + axis.dataMin = axis.dataMax = null; + resetMinMax = false; + } + + + var yData = series.processedYData, + yDataLength = yData.length, + seriesDataMin = yData[0], + seriesDataMax = yData[0], + threshold = series.options.threshold, + stacks = axis.stacks, + stackKey = series.stackKey, + negKey = '-' + stackKey, + total, + previous, + key, + i; + + + // set new stack totals including preceding values, finds new min and max values + for (i = 0; i < yDataLength; i++) { + key = yData[i] < threshold ? negKey : stackKey; + total = stacks[key][i].total; + + if (i > threshold) { + total += previous; + stacks[key][i].setTotal(total); + + // _cum is used to avoid conflict with Series.translate method + stacks[key][i]._cum = null; + } + + + // find min / max values + if (total < seriesDataMin) { + seriesDataMin = total; + } + + if (total > seriesDataMax) { + seriesDataMax = total; + } + + previous = total; + } + + + // set new extremes + series.dataMin = seriesDataMin; + series.dataMax = seriesDataMax; + axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin, threshold); + axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax, threshold); + + // remember series' stack key + visitedStacks.push(series.stackKey); + + + + // Adjust to threshold. This code is duplicated from the parent getSeriesExtremes method. + if (typeof threshold === 'number') { + if (axis.dataMin >= threshold) { + axis.dataMin = threshold; + axis.ignoreMinPadding = true; + } else if (axis.dataMax < threshold) { + axis.dataMax = threshold; + axis.ignoreMaxPadding = true; + } + } + }); +}); + + +// 1 - set default options +defaultPlotOptions.waterfall = merge(defaultPlotOptions.column, { + lineWidth: 1, + lineColor: '#333', + dashStyle: 'dot', + borderColor: '#333' +}); + + +// 2 - Create the series object +seriesTypes.waterfall = extendClass(seriesTypes.column, { + type: 'waterfall', + + upColorProp: 'fill', + + pointArrayMap: ['y', 'low'], + + pointValKey: 'y', + + /** + * Init waterfall series, force stacking + */ + init: function (chart, options) { + options.stacking = true; + seriesTypes.column.prototype.init.call(this, chart, options); + }, + + + /** + * Translate data points from raw values + */ + translate: function () { + var series = this, + options = series.options, + axis = series.yAxis, + len, + i, + + points, + point, + shapeArgs, + sum, + sumStart, + subSum, + subSumStart, + edges, + cumulative, + prevStack, + prevY, + stack, + crispCorr = (options.borderWidth % 2) / 2; + + // run column series translate + seriesTypes.column.prototype.translate.apply(this); + + + points = this.points; + subSumStart = sumStart = points[0]; + sum = subSum = points[0].y; + + for (i = 1, len = points.length; i < len; i++) { + // cache current point object + point = points[i]; + shapeArgs = point.shapeArgs; + + // get current and previous stack + stack = series.getStack(i); + prevStack = series.getStack(i - 1); + prevY = series.getStackY(prevStack); + + // set new intermediate sum values after reset + if (subSumStart === null) { + subSumStart = point; + subSum = 0; + } + + // sum only points with value, not intermediate or total sum + if (point.y && !point.isSum && !point.isIntermediateSum) { + sum += point.y; + subSum += point.y; + } + + // calculate sum points + if (point.isSum || point.isIntermediateSum) { + + if (point.isIntermediateSum) { + edges = series.getSumEdges(subSumStart, points[i - 1]); + point.y = subSum; + subSumStart = null; + } else { + edges = series.getSumEdges(sumStart, points[i - 1]); + point.y = sum; + } + + shapeArgs.y = point.plotY = edges[1]; + shapeArgs.height = edges[0] - edges[1]; + + // calculate other (up or down) points based on y value + } else { + // use "_cum" instead of already calculated "cum" to avoid reverse ordering negative columns + cumulative = stack._cum === null ? prevStack.total : stack._cum; + stack._cum = cumulative + point.y; + + if (point.y < 0) { + shapeArgs.y = mathCeil(axis.translate(cumulative, 0, 1)) - crispCorr; + shapeArgs.height = mathCeil(axis.translate(stack._cum, 0, 1) - shapeArgs.y); + } else { + if (prevStack.total + point.y < 0) { + shapeArgs.y = axis.translate(stack._cum, 0, 1); + } + + shapeArgs.height = mathFloor(prevY - shapeArgs.y); + } + + } + } + }, + + /** + * Call default processData then override yData to reflect waterfall's extremes on yAxis + */ + processData: function (force) { + Series.prototype.processData.call(this, force); + + var series = this, + options = series.options, + yData = series.yData, + length = yData.length, + prev, + curr, + subSum, + sum, + i; + + prev = sum = subSum = options.threshold; + + for (i = 0; i < length; i++) { + curr = yData[i]; + + // processed yData only if it's not already processed + if (curr !== null && typeof curr !== 'number') { + + if (curr === "sum") { + yData[i] = null; + + } else if (curr === "intermediateSum") { + yData[i] = null; + subSum = prev; + + } else { + yData[i] = curr[0];// + prev; + } + + prev = yData[i]; + } + } + }, + + /** + * Return [y, low] array, if low is not defined, it's replaced with null for further calculations + */ + toYData: function (pt) { + if (pt.isSum) { + return "sum"; + } else if (pt.isIntermediateSum) { + return "intermediateSum"; + } + + return [pt.y]; + }, + + /** + * Postprocess mapping between options and SVG attributes + */ + getAttribs: function () { + seriesTypes.column.prototype.getAttribs.apply(this, arguments); + + var series = this, + options = series.options, + stateOptions = options.states, + upColor = options.upColor || series.color, + hoverColor = Highcharts.Color(upColor).brighten(0.1).get(), + seriesDownPointAttr = merge(series.pointAttr), + upColorProp = series.upColorProp; + + seriesDownPointAttr[''][upColorProp] = upColor; + seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || hoverColor; + seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor; + + each(series.points, function (point) { + if (point.y > 0 && !point.color) { + point.pointAttr = seriesDownPointAttr; + point.color = upColor; + } + }); + }, + + /** + * Draw columns' connector lines + */ + getGraphPath: function () { + + var data = this.data, + length = data.length, + lineWidth = this.options.lineWidth + this.options.borderWidth, + normalizer = mathRound(lineWidth) % 2 / 2, + path = [], + M = 'M', + L = 'L', + prevArgs, + pointArgs, + i, + d; + + for (i = 1; i < length; i++) { + pointArgs = data[i].shapeArgs; + prevArgs = data[i - 1].shapeArgs; + + d = [ + M, + prevArgs.x + prevArgs.width, prevArgs.y + normalizer, + L, + pointArgs.x, prevArgs.y + normalizer + ]; + + if (data[i - 1].y < 0) { + d[2] += prevArgs.height; + d[5] += prevArgs.height; + } + + path = path.concat(d); + } + + return path; + }, + + getStack: function (i) { + var axis = this.yAxis, + stacks = axis.stacks, + key = this.stackKey; + + if (this.processedYData[i] < this.options.threshold) { + key = '-' + key; + } + + return stacks[key][i]; + }, + + getStackY: function (stack) { + return mathCeil(this.yAxis.translate(stack.total, null, true)); + }, + + /** + * Return array of top and bottom position for sum column based on given edge points + */ + getSumEdges: function (pointA, pointB) { + var valueA, + valueB, + tmp, + threshold = this.options.threshold; + + valueA = pointA.y >= threshold ? pointA.shapeArgs.y + pointA.shapeArgs.height : pointA.shapeArgs.y; + valueB = pointB.y >= threshold ? pointB.shapeArgs.y : pointB.shapeArgs.y + pointB.shapeArgs.height; + + if (valueB > valueA) { + tmp = valueA; + valueA = valueB; + valueB = tmp; + } + + return [valueA, valueB]; + }, + + drawGraph: Series.prototype.drawGraph +}); + +/* **************************************************************************** + * End Waterfall series code * + *****************************************************************************/ +/* **************************************************************************** + * Start Bubble series code * + *****************************************************************************/ + +// 1 - set default options +defaultPlotOptions.bubble = merge(defaultPlotOptions.scatter, { + dataLabels: { + inside: true, + style: { + color: 'white', + textShadow: '0px 0px 3px black' + }, + verticalAlign: 'middle' + }, + // displayNegative: true, + marker: { + // fillOpacity: 0.5, + lineColor: null, // inherit from series.color + lineWidth: 1 + }, + minSize: 8, + maxSize: '20%', + // negativeColor: null, + tooltip: { + pointFormat: '({point.x}, {point.y}), Size: {point.z}' + }, + zThreshold: 0 +}); + +// 2 - Create the series object +seriesTypes.bubble = extendClass(seriesTypes.scatter, { + type: 'bubble', + pointArrayMap: ['y', 'z'], + trackerGroups: ['group', 'dataLabelsGroup'], + + /** + * Mapping between SVG attributes and the corresponding options + */ + pointAttrToOptions: { + stroke: 'lineColor', + 'stroke-width': 'lineWidth', + fill: 'fillColor' + }, + + /** + * Apply the fillOpacity to all fill positions + */ + applyOpacity: function (fill) { + var markerOptions = this.options.marker, + fillOpacity = pick(markerOptions.fillOpacity, 0.5); + + // When called from Legend.colorizeItem, the fill isn't predefined + fill = fill || markerOptions.fillColor || this.color; + + if (fillOpacity !== 1) { + fill = Highcharts.Color(fill).setOpacity(fillOpacity).get('rgba'); + } + return fill; + }, + + /** + * Extend the convertAttribs method by applying opacity to the fill + */ + convertAttribs: function () { + var obj = Series.prototype.convertAttribs.apply(this, arguments); + + obj.fill = this.applyOpacity(obj.fill); + + return obj; + }, + + /** + * Get the radius for each point based on the minSize, maxSize and each point's Z value. This + * must be done prior to Series.translate because the axis needs to add padding in + * accordance with the point sizes. + */ + getRadii: function (zMin, zMax, minSize, maxSize) { + var len, + i, + pos, + zData = this.zData, + radii = [], + zRange; + + // Set the shape type and arguments to be picked up in drawPoints + for (i = 0, len = zData.length; i < len; i++) { + zRange = zMax - zMin; + pos = zRange > 0 ? // relative size, a number between 0 and 1 + (zData[i] - zMin) / (zMax - zMin) : + 0.5; + radii.push(math.ceil(minSize + pos * (maxSize - minSize)) / 2); + } + this.radii = radii; + }, + + /** + * Perform animation on the bubbles + */ + animate: function (init) { + var animation = this.options.animation; + + if (!init) { // run the animation + each(this.points, function (point) { + var graphic = point.graphic, + shapeArgs = point.shapeArgs; + + if (graphic && shapeArgs) { + // start values + graphic.attr('r', 1); + + // animate + graphic.animate({ + r: shapeArgs.r + }, animation); + } + }); + + // delete this function to allow it only once + this.animate = null; + } + }, + + /** + * Extend the base translate method to handle bubble size + */ + translate: function () { + + var i, + data = this.data, + point, + radius, + radii = this.radii; + + // Run the parent method + seriesTypes.scatter.prototype.translate.call(this); + + // Set the shape type and arguments to be picked up in drawPoints + i = data.length; + + while (i--) { + point = data[i]; + radius = radii ? radii[i] : 0; // #1737 + + // Flag for negativeColor to be applied in Series.js + point.negative = point.z < (this.options.zThreshold || 0); + + if (radius >= this.minPxSize / 2) { + // Shape arguments + point.shapeType = 'circle'; + point.shapeArgs = { + x: point.plotX, + y: point.plotY, + r: radius + }; + + // Alignment box for the data label + point.dlBox = { + x: point.plotX - radius, + y: point.plotY - radius, + width: 2 * radius, + height: 2 * radius + }; + } else { // below zThreshold + point.shapeArgs = point.plotY = point.dlBox = UNDEFINED; // #1691 + } + } + }, + + /** + * Get the series' symbol in the legend + * + * @param {Object} legend The legend object + * @param {Object} item The series (this) or point + */ + drawLegendSymbol: function (legend, item) { + var radius = pInt(legend.itemStyle.fontSize) / 2; + + item.legendSymbol = this.chart.renderer.circle( + radius, + legend.baseline - radius, + radius + ).attr({ + zIndex: 3 + }).add(item.legendGroup); + + }, + + drawPoints: seriesTypes.column.prototype.drawPoints, + alignDataLabel: seriesTypes.column.prototype.alignDataLabel +}); + +/** + * Add logic to pad each axis with the amount of pixels + * necessary to avoid the bubbles to overflow. + */ +Axis.prototype.beforePadding = function () { + var axis = this, + axisLength = this.len, + chart = this.chart, + pxMin = 0, + pxMax = axisLength, + isXAxis = this.isXAxis, + dataKey = isXAxis ? 'xData' : 'yData', + min = this.min, + extremes = {}, + smallestSize = math.min(chart.plotWidth, chart.plotHeight), + zMin = Number.MAX_VALUE, + zMax = -Number.MAX_VALUE, + range = this.max - min, + transA = axisLength / range, + activeSeries = []; + + // Handle padding on the second pass, or on redraw + if (this.tickPositions) { + each(this.series, function (series) { + + var seriesOptions = series.options, + zData; + + if (series.type === 'bubble' && series.visible) { + + // Correction for #1673 + axis.allowZoomOutside = true; + + // Cache it + activeSeries.push(series); + + if (isXAxis) { // because X axis is evaluated first + + // For each series, translate the size extremes to pixel values + each(['minSize', 'maxSize'], function (prop) { + var length = seriesOptions[prop], + isPercent = /%$/.test(length); + + length = pInt(length); + extremes[prop] = isPercent ? + smallestSize * length / 100 : + length; + + }); + series.minPxSize = extremes.minSize; + + // Find the min and max Z + zData = series.zData; + if (zData.length) { // #1735 + zMin = math.min( + zMin, + math.max( + arrayMin(zData), + seriesOptions.displayNegative === false ? seriesOptions.zThreshold : -Number.MAX_VALUE + ) + ); + zMax = math.max(zMax, arrayMax(zData)); + } + } + } + }); + + each(activeSeries, function (series) { + + var data = series[dataKey], + i = data.length, + radius; + + if (isXAxis) { + series.getRadii(zMin, zMax, extremes.minSize, extremes.maxSize); + } + + if (range > 0) { + while (i--) { + radius = series.radii[i]; + pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin); + pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax); + } + } + }); + + if (range > 0 && pick(this.options.min, this.userMin) === UNDEFINED && pick(this.options.max, this.userMax) === UNDEFINED) { + pxMax -= axisLength; + transA *= (axisLength + pxMin - pxMax) / axisLength; + this.min += pxMin / transA; + this.max += pxMax / transA; + } + } +}; + +/* **************************************************************************** + * End Bubble series code * + *****************************************************************************/ +/** + * Extensions for polar charts. Additionally, much of the geometry required for polar charts is + * gathered in RadialAxes.js. + * + */ + +var seriesProto = Series.prototype, + pointerProto = Highcharts.Pointer.prototype; + + + +/** + * Translate a point's plotX and plotY from the internal angle and radius measures to + * true plotX, plotY coordinates + */ +seriesProto.toXY = function (point) { + var xy, + chart = this.chart, + plotX = point.plotX, + plotY = point.plotY; + + // Save rectangular plotX, plotY for later computation + point.rectPlotX = plotX; + point.rectPlotY = plotY; + + // Record the angle in degrees for use in tooltip + point.clientX = plotX / Math.PI * 180; + + // Find the polar plotX and plotY + xy = this.xAxis.postTranslate(point.plotX, this.yAxis.len - plotY); + point.plotX = point.polarPlotX = xy.x - chart.plotLeft; + point.plotY = point.polarPlotY = xy.y - chart.plotTop; +}; + + +/** + * Add some special init logic to areas and areasplines + */ +function initArea(proceed, chart, options) { + proceed.call(this, chart, options); + if (this.chart.polar) { + + /** + * Overridden method to close a segment path. While in a cartesian plane the area + * goes down to the threshold, in the polar chart it goes to the center. + */ + this.closeSegment = function (path) { + var center = this.xAxis.center; + path.push( + 'L', + center[0], + center[1] + ); + }; + + // Instead of complicated logic to draw an area around the inner area in a stack, + // just draw it behind + this.closedStacks = true; + } +} +wrap(seriesTypes.area.prototype, 'init', initArea); +wrap(seriesTypes.areaspline.prototype, 'init', initArea); + + +/** + * Overridden method for calculating a spline from one point to the next + */ +wrap(seriesTypes.spline.prototype, 'getPointSpline', function (proceed, segment, point, i) { + + var ret, + smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc; + denom = smoothing + 1, + plotX, + plotY, + lastPoint, + nextPoint, + lastX, + lastY, + nextX, + nextY, + leftContX, + leftContY, + rightContX, + rightContY, + distanceLeftControlPoint, + distanceRightControlPoint, + leftContAngle, + rightContAngle, + jointAngle; + + + if (this.chart.polar) { + + plotX = point.plotX; + plotY = point.plotY; + lastPoint = segment[i - 1]; + nextPoint = segment[i + 1]; + + // Connect ends + if (this.connectEnds) { + if (!lastPoint) { + lastPoint = segment[segment.length - 2]; // not the last but the second last, because the segment is already connected + } + if (!nextPoint) { + nextPoint = segment[1]; + } + } + + // find control points + if (lastPoint && nextPoint) { + + lastX = lastPoint.plotX; + lastY = lastPoint.plotY; + nextX = nextPoint.plotX; + nextY = nextPoint.plotY; + leftContX = (smoothing * plotX + lastX) / denom; + leftContY = (smoothing * plotY + lastY) / denom; + rightContX = (smoothing * plotX + nextX) / denom; + rightContY = (smoothing * plotY + nextY) / denom; + distanceLeftControlPoint = Math.sqrt(Math.pow(leftContX - plotX, 2) + Math.pow(leftContY - plotY, 2)); + distanceRightControlPoint = Math.sqrt(Math.pow(rightContX - plotX, 2) + Math.pow(rightContY - plotY, 2)); + leftContAngle = Math.atan2(leftContY - plotY, leftContX - plotX); + rightContAngle = Math.atan2(rightContY - plotY, rightContX - plotX); + jointAngle = (Math.PI / 2) + ((leftContAngle + rightContAngle) / 2); + + + // Ensure the right direction, jointAngle should be in the same quadrant as leftContAngle + if (Math.abs(leftContAngle - jointAngle) > Math.PI / 2) { + jointAngle -= Math.PI; + } + + // Find the corrected control points for a spline straight through the point + leftContX = plotX + Math.cos(jointAngle) * distanceLeftControlPoint; + leftContY = plotY + Math.sin(jointAngle) * distanceLeftControlPoint; + rightContX = plotX + Math.cos(Math.PI + jointAngle) * distanceRightControlPoint; + rightContY = plotY + Math.sin(Math.PI + jointAngle) * distanceRightControlPoint; + + // Record for drawing in next point + point.rightContX = rightContX; + point.rightContY = rightContY; + + } + + + // moveTo or lineTo + if (!i) { + ret = ['M', plotX, plotY]; + } else { // curve from last point to this + ret = [ + 'C', + lastPoint.rightContX || lastPoint.plotX, + lastPoint.rightContY || lastPoint.plotY, + leftContX || plotX, + leftContY || plotY, + plotX, + plotY + ]; + lastPoint.rightContX = lastPoint.rightContY = null; // reset for updating series later + } + + + } else { + ret = proceed.call(this, segment, point, i); + } + return ret; +}); + +/** + * Extend translate. The plotX and plotY values are computed as if the polar chart were a + * cartesian plane, where plotX denotes the angle in radians and (yAxis.len - plotY) is the pixel distance from + * center. + */ +wrap(seriesProto, 'translate', function (proceed) { + + // Run uber method + proceed.call(this); + + // Postprocess plot coordinates + if (this.chart.polar && !this.preventPostTranslate) { + var points = this.points, + i = points.length; + while (i--) { + // Translate plotX, plotY from angle and radius to true plot coordinates + this.toXY(points[i]); + } + } +}); + +/** + * Extend getSegmentPath to allow connecting ends across 0 to provide a closed circle in + * line-like series. + */ +wrap(seriesProto, 'getSegmentPath', function (proceed, segment) { + + var points = this.points; + + // Connect the path + if (this.chart.polar && this.options.connectEnds !== false && + segment[segment.length - 1] === points[points.length - 1] && points[0].y !== null) { + this.connectEnds = true; // re-used in splines + segment = [].concat(segment, [points[0]]); + } + + // Run uber method + return proceed.call(this, segment); + +}); + + +function polarAnimate(proceed, init) { + var chart = this.chart, + animation = this.options.animation, + group = this.group, + markerGroup = this.markerGroup, + center = this.xAxis.center, + plotLeft = chart.plotLeft, + plotTop = chart.plotTop, + attribs; + + // Specific animation for polar charts + if (chart.polar) { + + // Enable animation on polar charts only in SVG. In VML, the scaling is different, plus animation + // would be so slow it would't matter. + if (chart.renderer.isSVG) { + + if (animation === true) { + animation = {}; + } + + // Initialize the animation + if (init) { + + // Scale down the group and place it in the center + attribs = { + translateX: center[0] + plotLeft, + translateY: center[1] + plotTop, + scaleX: 0.001, // #1499 + scaleY: 0.001 + }; + + group.attr(attribs); + if (markerGroup) { + markerGroup.attrSetters = group.attrSetters; + markerGroup.attr(attribs); + } + + // Run the animation + } else { + attribs = { + translateX: plotLeft, + translateY: plotTop, + scaleX: 1, + scaleY: 1 + }; + group.animate(attribs, animation); + if (markerGroup) { + markerGroup.animate(attribs, animation); + } + + // Delete this function to allow it only once + this.animate = null; + } + } + + // For non-polar charts, revert to the basic animation + } else { + proceed.call(this, init); + } +} + +// Define the animate method for both regular series and column series and their derivatives +wrap(seriesProto, 'animate', polarAnimate); +wrap(colProto, 'animate', polarAnimate); + + +/** + * Throw in a couple of properties to let setTooltipPoints know we're indexing the points + * in degrees (0-360), not plot pixel width. + */ +wrap(seriesProto, 'setTooltipPoints', function (proceed, renew) { + + if (this.chart.polar) { + extend(this.xAxis, { + tooltipLen: 360 // degrees are the resolution unit of the tooltipPoints array + }); + } + + // Run uber method + return proceed.call(this, renew); +}); + + +/** + * Extend the column prototype's translate method + */ +wrap(colProto, 'translate', function (proceed) { + + var xAxis = this.xAxis, + len = this.yAxis.len, + center = xAxis.center, + startAngleRad = xAxis.startAngleRad, + renderer = this.chart.renderer, + start, + points, + point, + i; + + this.preventPostTranslate = true; + + // Run uber method + proceed.call(this); + + // Postprocess plot coordinates + if (xAxis.isRadial) { + points = this.points; + i = points.length; + while (i--) { + point = points[i]; + start = point.barX + startAngleRad; + point.shapeType = 'path'; + point.shapeArgs = { + d: renderer.symbols.arc( + center[0], + center[1], + len - point.plotY, + null, + { + start: start, + end: start + point.pointWidth, + innerR: len - pick(point.yBottom, len) + } + ) + }; + this.toXY(point); // provide correct plotX, plotY for tooltip + } + } +}); + + +/** + * Align column data labels outside the columns. #1199. + */ +wrap(colProto, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo, isNew) { + + if (this.chart.polar) { + var angle = point.rectPlotX / Math.PI * 180, + align, + verticalAlign; + + // Align nicely outside the perimeter of the columns + if (options.align === null) { + if (angle > 20 && angle < 160) { + align = 'left'; // right hemisphere + } else if (angle > 200 && angle < 340) { + align = 'right'; // left hemisphere + } else { + align = 'center'; // top or bottom + } + options.align = align; + } + if (options.verticalAlign === null) { + if (angle < 45 || angle > 315) { + verticalAlign = 'bottom'; // top part + } else if (angle > 135 && angle < 225) { + verticalAlign = 'top'; // bottom part + } else { + verticalAlign = 'middle'; // left or right + } + options.verticalAlign = verticalAlign; + } + + seriesProto.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); + } else { + proceed.call(this, point, dataLabel, options, alignTo, isNew); + } + +}); + +/** + * Extend the mouse tracker to return the tooltip position index in terms of + * degrees rather than pixels + */ +wrap(pointerProto, 'getIndex', function (proceed, e) { + var ret, + chart = this.chart, + center, + x, + y; + + if (chart.polar) { + center = chart.xAxis[0].center; + x = e.chartX - center[0] - chart.plotLeft; + y = e.chartY - center[1] - chart.plotTop; + + ret = 180 - Math.round(Math.atan2(x, y) / Math.PI * 180); + + } else { + + // Run uber method + ret = proceed.call(this, e); + } + return ret; +}); + +/** + * Extend getCoordinates to prepare for polar axis values + */ +wrap(pointerProto, 'getCoordinates', function (proceed, e) { + var chart = this.chart, + ret = { + xAxis: [], + yAxis: [] + }; + + if (chart.polar) { + + each(chart.axes, function (axis) { + var isXAxis = axis.isXAxis, + center = axis.center, + x = e.chartX - center[0] - chart.plotLeft, + y = e.chartY - center[1] - chart.plotTop; + + ret[isXAxis ? 'xAxis' : 'yAxis'].push({ + axis: axis, + value: axis.translate( + isXAxis ? + Math.PI - Math.atan2(x, y) : // angle + Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), // distance from center + true + ) + }); + }); + + } else { + ret = proceed.call(this, e); + } + + return ret; +}); +}(Highcharts)); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highstock.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highstock.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highstock.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highstock.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,295 +1,334 @@ /* - Highstock JS v1.1.6 (2012-06-08) + Highstock JS v1.3.2 (2013-06-05) - (c) 2009-2011 Torstein H?nsi + (c) 2009-2013 Torstein Hønsi License: www.highcharts.com/license */ -(function(){function z(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function ha(){for(var a=0,b=arguments,c=b.length,d={};a-1?b.split(".")[1].length:0):a=isNaN(b=W(b))?2:b;var b=a,c=c===void 0?e.decimalPoint:c,d=d===void 0?e.thousandsSep:d,e=f<0?"-":"",a=String(F(f=W(+f||0).toFixed(b))),g=a.length>3?a.length%3:0;return e+(g?a.substr(0,g)+d:"")+ -a.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(b?c+W(f-a).toFixed(b).slice(2):"")}function za(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function pb(a,b,c,d){var e,c=p(c,1);e=a/c;b||(b=[1,2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=E[Za]&&(k.setMilliseconds(0),k.setSeconds(b>=E[La]?0:i*$(k.getSeconds()/i)));if(b>=E[La])k[Db](b>=E[sa]?0:i*$(k[qb]()/i)); -if(b>=E[sa])k[Eb](b>=E[fa]?0:i*$(k[rb]()/i));if(b>=E[fa])k[sb](b>=E[Ba]?1:i*$(k[Ca]()/i));b>=E[Ba]&&(k[Fb](b>=E[na]?0:i*$(k[hb]()/i)),h=k[ib]());b>=E[na]&&(h-=h%i,k[Gb](h));if(b===E[Aa])k[sb](k[Ca]()-k[tb]()+p(d,1));d=1;h=k[ib]();for(var j=k.getTime(),l=k[hb](),m=k[Ca](),k=g?0:(864E5+k.getTimezoneOffset()*6E4)%864E5;jc&&(c=a[b]);return c}function ta(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Na(a){kb||(kb=V(oa)); -a&&kb.appendChild(a);kb.innerHTML=""}function ub(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else Y.console&&console.log(c)}function pa(a){return parseFloat(a.toPrecision(14))}function Ea(a,b){$a=p(a,b.animation)}function Ib(){var a=T.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";jb=a?Date.UTC:function(a,b,c,g,h,k){return(new Date(a,b,p(c,1),p(g,0),p(h,0),p(k,0))).getTime()};qb=b+"Minutes";rb=b+"Hours";tb=b+"Day";Ca=b+"Date";hb=b+"Month";ib=b+"FullYear";Db= -c+"Minutes";Eb=c+"Hours";sb=c+"Date";Fb=c+"Month";Gb=c+"FullYear"}function Fa(){}function ab(a,b,c){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;c||this.addLabel()}function vb(a,b){this.axis=a;if(b)this.options=b,this.id=b.id;return this}function Jb(a,b,c,d,e){var f=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.alignOptions={align:b.align||(f?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(f?"middle":c?"bottom":"top"),y:p(b.y,f?4:c?14: --6),x:p(b.x,f?c?-6:6:0)};this.textAlign=b.textAlign||(f?c?"right":"left":"center")}function bb(){this.init.apply(this,arguments)}function wb(a,b){var c=b.borderWidth,d=b.style,e=b.shared,f=F(d.padding);this.chart=a;this.options=b;d.padding=0;this.crosshairs=[];this.currentY=this.currentX=0;this.tooltipIsHidden=!0;this.label=a.renderer.label("",0,0,null,null,null,b.useHTML,null,"tooltip").attr({padding:f,fill:b.backgroundColor,"stroke-width":c,r:b.borderRadius,zIndex:8}).css(d).hide().add();ma||this.label.shadow(b.shadow); -this.shared=e}function Kb(a,b){var c=ma?"":b.chart.zoomType;this.zoomX=/x/.test(c);this.zoomY=/y/.test(c);this.options=b;this.chart=a;this.init(a,b.tooltip)}function xb(a){this.init(a)}function cb(a,b){var c,d=a.series;a.series=null;c=y(T,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=ia(e)?e:[e,e,e,e];this.optionsMarginTop=p(d.marginTop,e[0]);this.optionsMarginRight=p(d.marginRight,e[1]);this.optionsMarginBottom=p(d.marginBottom,e[2]);this.optionsMarginLeft=p(d.marginLeft,e[3]);this.runChartClick= -(e=d.events)&&!!e.click;this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;this.init(e)}function Lb(a){var b=a.options,c=b.navigator,d=c.enabled,b=b.scrollbar,e=b.enabled,f=d?c.height:0,g=e?b.height:0,h=c.baseSeries;this.baseSeries=a.series[h]||typeof h==="string"&&a.get(h)||a.series[0];this.handles=[];this.scrollbarButtons=[];this.elementsToDestroy=[];a.resetZoomEnabled=!1;this.chart=a;this.height=f;this.scrollbarHeight=g;this.scrollbarEnabled= -e;this.navigatorEnabled=d;this.navigatorOptions=c;this.scrollbarOptions=b;this.outlineHeight=f+g;this.init()}function Mb(a){a.resetZoomEnabled=!1;this.chart=a;this.buttons=[];this.boxSpanElements={};this.init([{type:"month",count:1,text:"1m"},{type:"month",count:3,text:"3m"},{type:"month",count:6,text:"6m"},{type:"ytd",text:"YTD"},{type:"year",count:1,text:"1y"},{type:"all",text:"All"}])}var v,I=document,Y=window,R=Math,u=R.round,$=R.floor,Ga=R.ceil,t=R.max,O=R.min,W=R.abs,ga=R.cos,ka=R.sin,Ha=R.PI, -Nb=Ha*2/360,Ia=navigator.userAgent,Xa=/msie/i.test(Ia)&&!Y.opera,Oa=I.documentMode===8,Ob=/AppleWebKit/.test(Ia),Pb=/Firefox/.test(Ia),Pa=!!I.createElementNS&&!!I.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect,bc=Pb&&parseInt(Ia.split("Firefox/")[1],10)<4,ma=!Pa&&!Xa&&!!I.createElement("canvas").getContext,Qa,aa=I.documentElement.ontouchstart!==v,Qb={},yb=0,kb,T,ua,$a,db,E,Rb=function(){},oa="div",da="none",zb="rgba(192,192,192,"+(Pa?1.0E-6:0.0020)+")",fb="millisecond",Za="second", -La="minute",sa="hour",fa="day",Aa="week",Ba="month",na="year",jb,qb,rb,tb,Ca,hb,ib,Db,Eb,sb,Fb,Gb,U={};Y.Highcharts={};ua=function(a,b,c){if(!s(b)||isNaN(b))return"Invalid date";var a=p(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[rb](),g=d[tb](),h=d[Ca](),k=d[hb](),i=d[ib](),j=T.lang,l=j.weekdays,b={a:l[g].substr(0,3),A:l[g],d:za(h),e:h,b:j.shortMonths[k],B:j.months[k],m:za(k+1),y:i.toString().substr(2,2),Y:i,H:za(f),I:za(f%12||12),l:f%12||12,M:za(d[qb]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:za(d.getSeconds()), -L:za(u(b%1E3),3)};for(e in b)a=a.replace("%"+e,b[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Hb.prototype={wrapColor:function(a){if(this.color>=a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};E=ha(fb,1,Za,1E3,La,6E4,sa,36E5,fa,864E5,Aa,6048E5,Ba,2592E6,na,31556952E3);db={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,k,i=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+ -2])};e&&(i(b),i(c));a.isArea&&(h=b.splice(b.length-6,6),k=c.splice(c.length-6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift=0;if(b.length)for(a=c.length;b.length{point.key}

    ',pointFormat:'{series.name}: {point.y}
    ', -shadow:!0,shared:ma,snap:aa?25:10,style:{color:"#333333",fontSize:"12px",padding:"5px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer",color:"#909090",fontSize:"10px"}}};var S=T.plotOptions,P=S.line;Ib();var wa=function(a){var b=[],c;(function(a){(c=/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(a))?b=[F(c[1]),F(c[2]), -F(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))&&(b=[F(c[1],16),F(c[2],16),F(c[3],16),1])})(a);return{get:function(c){return b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a},brighten:function(a){if(Wa(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=F(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},setOpacity:function(a){b[3]=a;return this}}};Fa.prototype={init:function(a,b){this.element=b==="span"?V(b): -I.createElementNS("http://www.w3.org/2000/svg",b);this.renderer=a;this.attrSetters={}},animate:function(a,b,c){b=p(b,$a,!0);Ra(this);if(b){b=y(b);if(c)b.complete=c;lb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName,k=this.renderer,i,j=this.attrSetters,l=this.shadows,m,o,r=this;ya(a)&&s(b)&&(c=a,a={},a[c]=b);if(ya(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),r=G(g,c)||this[c]||0,c!=="d"&&c!=="visibility"&&(r=parseFloat(r)); -else for(c in a)if(i=!1,d=a[c],e=j[c]&&j[c](d,c),e!==!1){e!==v&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text"){for(e=0;em&&/[ \-]/.test(b.innerText)&&(M(b,{width:m+"px",display:"block",whiteSpace:"normal"}),j=m),m=a.fontMetrics(b.style.fontSize).b, -q=o<0&&-j,C=r<0&&-l,A=o*r<0,q+=r*m*(A?1-h:h),C-=o*m*(i?A?h:1-h:1),k&&(q-=j*h*(o<0?-1:1),i&&(C-=l*h*(r<0?-1:1)),M(b,{textAlign:g})),this.xCorr=q,this.yCorr=C;M(b,{left:e+q+"px",top:f+C+"px"});this.cTT=J}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.inverted,d=this.rotation,e=[];c&&(a+=this.attr("width"),b+=this.attr("height"));(a||b)&&e.push("translate("+a+","+b+")");c?e.push("rotate(90) scale(-1,1)"):d&&e.push("rotate("+d+" "+(this.x||0)+ -" "+(this.y||0)+")");e.length&&G(this.element,"transform",e.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){a?(this.alignOptions=a,this.alignByTranslate=b,c||this.renderer.alignedObjects.push(this)):(a=this.alignOptions,b=this.alignByTranslate);var c=p(c,this.renderer),d=a.align,e=a.verticalAlign,f=(c.x||0)+(a.x||0),g=(c.y||0)+(a.y||0),h={};/^(right|center)$/.test(d)&&(f+=(c.width-(a.width||0))/{right:1,center:2}[d]);h[b?"translateX": -"x"]=u(f);/^(bottom|middle)$/.test(e)&&(g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1));h[b?"translateY":"y"]=u(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(a){var b,c,d=this.rotation;c=this.element;var e=d*Nb;if(c.namespaceURI==="http://www.w3.org/2000/svg"||this.renderer.forExport){try{b=c.getBBox?z({},c.getBBox()):{width:c.offsetWidth,height:c.offsetHeight}}catch(f){}if(!b||b.width<0)b={width:0,height:0};a=b.width;c=b.height;if(d)b.width= -W(c*ka(e))+W(a*ga(e)),b.height=W(c*ga(e))+W(a*ka(e))}else b=this.htmlGetBBox(a);return b},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=G(f,"zIndex"),h;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=F(g);if(c.handleZ)for(c=0;cg||!s(g)&&s(b))){d.insertBefore(f, -a);h=!0;break}h||d.appendChild(f);this.added=!0;N(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a=this,b=a.element||{},c=a.shadows,d=a.box,e,f;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=null;Ra(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(f=0;f/g,'').replace(/<(i|em)>/g,'').replace(//g,"").split(//g),d=b.childNodes,e=/style="([^"]+)"/,f=/href="([^"]+)"/,g=G(b,"x"),h=a.styles,k=h&&F(h.width),i=h&&h.lineHeight,j,h=d.length,l=[];h--;)b.removeChild(d[h]); -k&&!a.added&&this.box.appendChild(b);c[c.length-1]===""&&c.pop();n(c,function(c,d){var h,A=0,q,c=c.replace(//g,"|||");h=c.split("|||");n(h,function(c){if(c!==""||h.length===1){var m={},n=I.createElementNS("http://www.w3.org/2000/svg","tspan");e.test(c)&&G(n,"style",c.match(e)[1].replace(/(;| |^)color([ :])/,"$1fill$2"));f.test(c)&&(G(n,"onclick",'location.href="'+c.match(f)[1]+'"'),M(n,{cursor:"pointer"}));c=(c.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g, -"<").replace(/>/g,">");n.appendChild(I.createTextNode(c));A?m.dx=3:m.x=g;if(!A){if(d){!Pa&&a.renderer.forExport&&M(n,{display:"block"});q=Y.getComputedStyle&&F(Y.getComputedStyle(j,null).getPropertyValue("line-height"));if(!q||isNaN(q)){var p;if(!(p=i))if(!(p=j.offsetHeight))l[d]=b.getBBox().height,p=u(l[d]-(l[d-1]||0))||18;q=p}G(n,"dy",q)}j=n}G(n,m);b.appendChild(n);A++;if(k)for(var c=c.replace(/-/g,"- ").split(" "),D=[];c.length||D.length;)p=a.getBBox().width,m=p>k,!m||c.length===1?(c=D,D=[], -c.length&&(n=I.createElementNS("http://www.w3.org/2000/svg","tspan"),G(n,{dy:i||16,x:g}),b.appendChild(n),p>k&&(k=p))):(n.removeChild(n.firstChild),D.unshift(c.pop())),c.length&&n.appendChild(I.createTextNode(c.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c),k=0,i,j,l,m,o,a={x1:0,y1:0,x2:0,y2:1},e=y(ha("stroke-width",1,"stroke","#999","fill",ha("linearGradient",a,"stops",[[0,"#FFF"],[1,"#DDD"]]),"r",3,"padding",3,"style",ha("color","black")),e);l=e.style; -delete e.style;f=y(e,ha("stroke","#68A","fill",ha("linearGradient",a,"stops",[[0,"#FFF"],[1,"#ACF"]])),f);m=f.style;delete f.style;g=y(e,ha("stroke","#68A","fill",ha("linearGradient",a,"stops",[[0,"#9BD"],[1,"#CDF"]])),g);o=g.style;delete g.style;B(h.element,"mouseenter",function(){h.attr(f).css(m)});B(h.element,"mouseleave",function(){i=[e,f,g][k];j=[l,m,o][k];h.attr(i).css(j)});h.setState=function(a){(k=a)?a===2&&h.attr(g).css(o):h.attr(e).css(l)};return h.on("click",function(){d.call(h)}).attr(e).css(z({cursor:"default"}, -l))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=u(a[1])+b%2/2);a[2]===a[5]&&(a[2]=a[5]=u(a[2])+b%2/2);return a},path:function(a){var b={fill:da};Va(a)?b.d=a:ia(a)&&z(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=ia(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(ia(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e= -ia(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:da});return e.attr(ia(a)?a:e.crisp(f,a,b,t(c,0),t(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects,e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return s(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:da};arguments.length>1&&z(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f); -f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f},symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(u(b),u(c),d,e,f),k=/^url\((.*?)\)$/,i,j;h?(g=this.path(h),z(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&z(g,f)):k.test(a)&&(j=function(a,b){a.attr({width:b[0],height:b[1]});a.alignByTranslate||a.translate(-u(b[0]/2),-u(b[1]/2))},i=a.match(k)[1],a=Qb[i],g=this.image(i).attr({x:b,y:c}),a?j(g,a):(g.attr({width:0, -height:0}),V("img",{onload:function(){j(g,Qb[i]=[this.width,this.height])},src:i})));return g},symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+ -c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-1.0E-6,d=e.innerR,h=e.open,k=ga(f),i=ka(f),j=ga(g),g=ka(g),e=e.end-f'); -if(b)c=b===oa||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=V(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer,c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);Oa&&d.gVis==="hidden"&&M(c,{visibility:"hidden"});d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();N(this,"add");return this},toggleChildren:function(a,b){for(var c=a.childNodes,d=c.length;d--;)M(c[d],{visibility:b}),c[d].nodeName=== -"DIV"&&this.toggleChildren(c[d],b)},updateTransform:Fa.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,k=this.renderer,i=this.symbolName,j,l=this.shadows,m,o=this.attrSetters,r=this;ya(a)&&s(b)&&(c=a,a={},a[c]=b);if(ya(a))c=a,r=c==="strokeWidth"||c==="stroke-width"?this.strokeweight:this[c];else for(c in a)if(d=a[c],m=!1,e=o[c]&&o[c](d,c),e!==!1&&d!==null){e!==v&&(d=e);if(i&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))j||(this.symbolAttr(a), -j=!0),m=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;for(m=[];e--;)m[e]=Wa(d[e])?u(d[e]*10)-5:d[e]==="Z"?"x":d[e];d=m.join(" ")||"x";f.path=d;if(l)for(e=l.length;e--;)l[e].path=l[e].cutOff?this.cutOffPath(d,l[e].cutOff):d;m=!0}else if(c==="zIndex"||c==="visibility"){if(Oa&&c==="visibility"&&h==="DIV")f.gVis=d,this.toggleChildren(f,d),d==="visible"&&(d=null);d&&(g[c]=d);m=!0}else if(c==="width"||c==="height")d=t(0,d),this[c]=d,this.updateClipping?(this[c]=d,this.updateClipping()):g[c]= -d,m=!0;else if(c==="x"||c==="y")this[c]=d,g[{x:"left",y:"top"}[c]]=d;else if(c==="class")f.className=d;else if(c==="stroke")d=k.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Wa(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||V(k.prepVML([""]),null,null,f))[c]=d||"solid",this.dashstyle=d,m=!0;else if(c==="fill")h==="SPAN"?g.color=d:(f.filled=d!==da?!0:!1,d=k.color(d,f,c),c="fillcolor");else if(h=== -"shape"&&c==="rotation")this[c]=d;else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),m=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,m=!0;if(l&&c==="visibility")for(e=l.length;e--;)l[e].style[c]=d;m||(Oa?f[c]=d:G(f,c,d))}return r},clip:function(a){var b=this,c=a.members,d=b.element,e=d.parentNode;c.push(b);b.destroyClip=function(){Ka(c,b)};e&&e.className==="highcharts-tracker"&&!Oa&&M(d,{visibility:"hidden"});return b.css(a.getCSS(b))},css:Fa.prototype.htmlCss, -safeRemoveChild:function(a){a.parentNode&&Na(a)},destroy:function(){this.destroyClip&&this.destroyClip();return Fa.prototype.destroy.apply(this)},empty:function(){for(var a=this.element.childNodes,b=a.length,c;b--;)c=a[b],c.parentNode.removeChild(c)},on:function(a,b){this.element["on"+a]=function(){var a=Y.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=F(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b, -c){var d=[],e=this.element,f=this.renderer,g,h=e.style,k,i=e.path,j,l;i&&typeof i.value!=="string"&&(i="x");l=i;if(a){for(a=1;a<=3;a++){j=7-2*a;c&&(l=this.cutOffPath(i.value,j+0.5));k=[''];g=V(f.prepVML(k),null,{left:F(h.left)+1,top:F(h.top)+1});if(c)g.cutOff=j+1;k=[''];V(f.prepVML(k),null,null,g);b?b.element.appendChild(g):e.parentNode.insertBefore(g, -e);d.push(g)}this.shadows=d}return this}},ea=ca(Fa,ea),ea={Element:ea,isIE8:Ia.indexOf("MSIE 8.0")>-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(oa);e=d.element;e.style.position="relative";a.appendChild(d.element);this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!I.namespaces.hcv)I.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),I.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "}, -isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e=this.createElement();return z(e,{members:[],left:a,top:b,width:c,height:d,getCSS:function(a){var b=a.inverted,c=this.top,d=this.left,e=d+this.width,j=c+this.height,c={clip:"rect("+u(b?d:c)+"px,"+u(b?j:e)+"px,"+u(b?e:j)+"px,"+u(b?c:d)+"px)"};!b&&Oa&&a.element.nodeName!=="IMG"&&z(c,{width:e+"px",height:j+"px"});return c},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c){var d, -e=/^rgba/,f,g=da;a&&a.linearGradient?f="gradient":a&&a.radialGradient&&(f="pattern");if(f){var h,k,i=a.linearGradient||a.radialGradient,j,l,m,o,r,A,q="",a=a.stops,p,J=[];l=a[0];p=a[a.length-1];l[0]>0&&a.unshift([0,l[1]]);p[0]<1&&a.push([1,p[1]]);n(a,function(a,b){e.test(a[1])?(d=wa(a[1]),h=d.get("rgb"),k=d.get("a")):(h=a[1],k=1);J.push(a[0]*100+"% "+h);b?(o=k,r=h):(m=k,A=h)});f==="gradient"?(j=i.x1||i[0]||0,a=i.y1||i[1]||0,l=i.x2||i[2]||0,i=i.y2||i[3]||0,j=90-R.atan((i-a)/(l-j))*180/Ha):(g=i.r*2, -q='src="http://code.highcharts.com/gfx/radial-gradient.png" size="'+g+","+g+'" origin="0.5,0.5" position="'+i.cx+","+i.cy+'" color2="'+A+'" ',g=r);c==="fill"?(c=[''],V(this.prepVML(c),null,null,b)):g=h}else if(e.test(a)&&b.tagName!=="IMG")d=wa(a),c=["<",c,' opacity="',d.get("a"),'"/>'],V(this.prepVML(c),null,null,b),g=d.get("rgb");else{b=b.getElementsByTagName(c);if(b.length)b[0].opacity= -1;g=a}return g},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="','style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.css({left:b,top:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(ia(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,t(c,0),t(d,0)))},invertChild:function(a, -b){var c=b.style;M(a,{flip:"x",left:F(c.width)-1,top:F(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=ga(f),d=ka(f),k=ga(g),i=ka(g),j=e.innerR,l=0.08/h,m=j&&0.1/j||0;if(g-f===0)return["x"];else 2*Ha-g+fi&&(c=!1)):h+j>m&&(h=m-j,d&&h+l0&&b.height>0){f=y({align:c&&j&&"center",x:c?!j&&4:10,verticalAlign:!c&&j&&"middle",y:c?j?16:10:j?6:-4,rotation:c&&!j&&90},f);if(!g)a.label=g=w.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:n}).css(f.style).add();b=[r[1],r[4],p(r[6],r[1])];r=[r[2],r[5],p(r[7],r[2])];c=Ma(b);j=Ma(r);g.align(f,!1,{x:c,y:j,width:Da(b)-c,height:Da(r)-j});g.show()}else g&&g.hide();return a},destroy:function(){Ka(this.axis.plotLinesAndBands, -this);ta(this,this.axis)}};Jb.prototype={destroy:function(){ta(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options.formatter.call(this);this.label?this.label.attr({text:b,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(b,0,0).css(this.options.style).attr({align:this.textAlign,rotation:this.options.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c=this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.total, -0,0,0,1),c=c.translate(0),c=W(g-c),h=d.xAxis[0].translate(this.x)+a,d=d.plotHeight,e={x:e?f?g:g-c:h,y:e?d-h-b:f?d-g-c:d-g,width:e?c:b,height:e?b:c};this.label&&this.label.align(this.alignOptions,null,e).attr({visibility:"visible"})}};bb.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S",minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:L,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01, -maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#6D869F",fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05, -minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Y-values"},stackLabels:{enabled:!1,formatter:function(){return this.total},style:L.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}},defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX; -this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type,f=e==="datetime";this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions=b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.categories=d.categories;this.isLog=e==="logarithmic";this.isLinked=s(d.linkedTo);this.isDatetimeAxis= -f;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0;this.stacks={};this.min=this.max=null;var g,d=this.options.events;a.axes.push(this);a[c?"xAxis":"yAxis"].push(this);this.series=[];if(a.inverted&&c&&this.reversed===v)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;this.addPlotLine=this.addPlotBand=this.addPlotBandOrLine;for(g in d)B(this, -g,d[g]);if(this.isLog)this.val2lin=ra,this.lin2val=ja},setOptions:function(a){this.options=y(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions,this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],a)},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.tickInterval,d=this.dateTimeLabelFormat;return a.categories?b:d?ua(d,b):c%1E6===0?b/1E6+"M":c%1E3===0?b/1E3+"k":b>=1E3?Ya(b,0):Ya(b,-1)},getSeriesExtremes:function(){var a= -this,b=a.chart,c=a.stacks,d=[],e=[],f;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var h=g.options,k,i,j,l,m,o,r,n,q,C=h.threshold,J,w=[],x=0;if(a.isLog&&C<=0)C=h.threshold=null;if(a.isXAxis){if(h=g.xData,h.length)a.dataMin=O(p(a.dataMin,h[0]),Ma(h)),a.dataMax=t(p(a.dataMax,h[0]),Da(h))}else{var D,K,H,u=g.cropped,Sa=g.xAxis.getExtremes(),y=!!g.modifyValue;k=h.stacking;a.usePercentage=k==="percent";if(k)m=h.stack,l=g.type+p(m,""),o="-"+l,g.stackKey= -l,i=d[l]||[],d[l]=i,j=e[o]||[],e[o]=j;if(a.usePercentage)a.dataMin=0,a.dataMax=99;h=g.processedXData;r=g.processedYData;J=r.length;for(f=0;f=Sa.min&&(h[f-1]||n)<=Sa.max))if(n=q.length)for(;n--;)q[n]!==null&&(w[x++]=q[n]);else w[x++]=q;if(!a.usePercentage&&w.length)a.dataMin=O(p(a.dataMin, -w[0]),Ma(w)),a.dataMax=t(p(a.dataMax,w[0]),Da(w));if(s(C))if(a.dataMin>=C)a.dataMin=C,a.ignoreMinPadding=!0;else if(a.dataMaxe+this.width)l=!0}else if(c=e,h=j-this.right,gf+this.height)l=!0;return l?null:d.renderer.crispLine(["M",c,g,"L",h,k],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a); -d&&c?d.push(c[4],c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=pa($(b/a)*a),c=pa(Ga(c/a)*a),e=[];b<=c;){e.push(b);b=pa(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=u(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=$(b),h,k,i,j,l,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&g.push(j),j>c&&(l=!0),j=i}else if(b=ja(b),c=ja(c),a=e[d?"minorTickInterval":"tickInterval"],a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=pb(a,null,R.pow(10,$(R.log(a)/R.LN10))),g=va(this.getLinearTickPositions(a,b,c),ra),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.tickPositions,b=this.minorTickInterval,c=[],d,e;if(this.isLog){e=a.length;for(d= -1;d=this.minRange,f,g,h,k,i;if(this.isXAxis&&this.minRange===v&&!this.isLog)s(a.min)||s(a.max)?this.minRange=null:(n(this.series,function(a){k=a.xData;for(g=i=a.xIncrement?1:k.length-1;g>0;g--)if(h=k[g]-k[g-1],f===v||h -0||!b.ignoreMaxPadding))b.max+=c*i}b.tickInterval=b.min===b.max||b.min===void 0||b.max===void 0?1:h&&!l&&m===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:p(l,o?1:(b.max-b.min)*m/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation();b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!f&&!e&&(a=R.pow(10,$(R.log(b.tickInterval)/ -R.LN10)),!s(d.tickInterval)))b.tickInterval=pb(b.tickInterval,null,a,d);b.minorTickInterval=d.minorTickInterval==="auto"&&b.tickInterval?b.tickInterval/5:d.minorTickInterval;b.tickPositions=k=d.tickPositions||k&&k.apply(b,[b.min,b.max]);if(!k)k=f?(b.getNonLinearTimeTicks||gb)(Cb(b.tickInterval,d.units),b.min,b.max,d.startOfWeek,b.ordinalPositions,b.closestPointRange,!0):e?b.getLogTickPositions(b.tickInterval,b.min,b.max):b.getLinearTickPositions(b.tickInterval,b.min,b.max),b.tickPositions=k;if(!h)e= -k[0],f=k[k.length-1],d.startOnTick?b.min=e:b.min>e&&k.shift(),d.endOnTick?b.max=f:b.maxb[d]&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this.xOrY,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&&!this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d= -this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(ea||a===null?a=c:b=a.min&&b<=a.max)i[b]||(i[b]=new ab(a,b)),q&&i[b].isNew&&i[b].render(c,!0),i[b].isActive=!0,i[b].render(c)}),o&&n(g,function(b,c){if(c%2===0&&b1||W(b-c.currentY)>1?function(){c.move(a,b)}:null},hide:function(){if(!this.tooltipIsHidden){var a=this.chart.hoverPoints;this.label.hide();a&&n(a,function(a){a.setState()});this.chart.hoverPoints=null;this.tooltipIsHidden=!0}},hideCrosshairs:function(){n(this.crosshairs, -function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e=d.inverted,f=0,g=0,a=la(a);c=a[0].tooltipPos;c||(n(a,function(a){f+=a.plotX;g+=a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY}),f/=a.length,g/=a.length,c=[e?d.plotWidth-g:f,this.shared&&!e&&a.length>1&&b?b.chartY-d.plotTop:e?d.plotHeight-f:g]);return va(c,u)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,k=p(this.options.distance,12),i=c.plotX,c=c.plotY,d=i+e+(d.inverted?k:-a-k), -j=c-b+f+15,l;d<7&&(d=e+i+k);d+a>e+g&&(d-=d+a-(e+g),j=c-b+f-k,l=!0);j=j&&c<=j+b&&(j=c+f+k));j+b>f+h&&(j=t(f,f+h-b-k));return{x:d,y:j}},refresh:function(a,b){function c(){var a=this.points||la(this),b=a[0].series,c;c=[b.tooltipHeaderFormatter(a[0].key)];n(a,function(a){b=a.series;c.push(b.tooltipFormatter&&b.tooltipFormatter(a)||a.point.tooltipFormatter(b.tooltipOptions.pointFormat))});c.push(f.footerFormat||"");return c.join("")}var d=this.chart,e=this.label,f=this.options,g,h,k, -i={},j,l=[];j=f.formatter||c;var i=d.hoverPoints,m,o=f.crosshairs;k=this.shared;h=this.getAnchor(a,b);g=h[0];h=h[1];k&&(!a.series||!a.series.noSharedTooltip)?(i&&n(i,function(a){a.setState()}),d.hoverPoints=a,n(a,function(a){a.setState("hover");l.push(a.getLabelConfig())}),i={x:a[0].category,y:a[0].y},i.points=l,a=a[0]):i=a.getLabelConfig();j=j.call(i);i=a.series;k=k||!i.isCartesian||i.tooltipOutsidePlot||d.isInsidePlot(g,h);j===!1||!k?this.hide():(this.tooltipIsHidden&&e.show(),e.attr({text:j}), -m=f.borderColor||a.color||i.color||"#606060",e.attr({stroke:m}),e=(f.positioner||this.getPosition).call(this,e.width,e.height,{plotX:g,plotY:h}),this.move(u(e.x),u(e.y)),this.tooltipIsHidden=!1);if(o){o=la(o);for(e=o.length;e--;)if(k=a.series[e?"yAxis":"xAxis"],o[e]&&k)if(k=k.getPlotLinePath(e?p(a.stackY,a.y):a.x,1),this.crosshairs[e])this.crosshairs[e].attr({d:k,visibility:"visible"});else{i={"stroke-width":o[e].width||1,stroke:o[e].color||"#C0C0C0",zIndex:o[e].zIndex||2};if(o[e].dashStyle)i.dashstyle= -o[e].dashStyle;this.crosshairs[e]=d.renderer.path(k).attr(i).add()}}N(d,"tooltipRefresh",{text:j,x:g+d.plotLeft,y:h+d.plotTop,borderColor:m})},tick:function(){this.tooltipTick&&this.tooltipTick()}};Kb.prototype={normalizeMouseEvent:function(a){var b,c,d,a=a||Y.event;if(!a.target)a.target=a.srcElement;if(a.originalEvent)a=a.originalEvent;if(a.event)a=a.event;d=a.touches?a.touches.item(0):a;this.chartPosition=b=Tb(this.chart.container);d.pageX===v?(c=a.x,b=a.y):(c=d.pageX-b.left,b=d.pageY-b.top);return z(a, -{chartX:u(c),chartY:u(b)})},getMouseCoordinates:function(a){var b={xAxis:[],yAxis:[]},c=this.chart;n(c.axes,function(d){var e=d.isXAxis;b[e?"xAxis":"yAxis"].push({axis:d,value:d.translate((c.inverted?!e:e)?a.chartX-c.plotLeft:c.plotHeight-a.chartY+c.plotTop,!0)})});return b},onmousemove:function(a){var b=this.chart,c=b.series,d,e,f=b.hoverPoint,g=b.hoverSeries,h,k,i=b.chartWidth,j=b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft;if(b.tooltip&&this.options.tooltip.shared&&(!g||!g.noSharedTooltip)){e= -[];h=c.length;for(k=0;ki&&e.splice(h,1);if(e.length&&e[0].plotX!==this.hoverX)b.tooltip.refresh(e,a),this.hoverX=e[0].plotX}if(g&&g.tracker&&(d=g.tooltipPoints[j])&&d!==f)d.onMouseOver()},resetTracker:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.hoverPoints||d,b=b.tooltip; -(a=a&&b&&e)&&la(e)[0].plotX===v&&(a=!1);if(a)b.refresh(e);else{if(d)d.onMouseOut();if(c)c.onMouseOut();b&&(b.hide(),b.hideCrosshairs());this.hoverX=null}},setDOMEvents:function(){function a(){if(b.selectionMarker){var f={xAxis:[],yAxis:[]},g=b.selectionMarker.getBBox(),h=g.x-c.plotLeft,l=g.y-c.plotTop,m;e&&(n(c.axes,function(a){if(a.options.zoomEnabled!==!1){var b=a.isXAxis,d=c.inverted?!b:b,e=a.translate(d?h:c.plotHeight-l-g.height,!0,0,0,1),d=a.translate(d?h+g.width:c.plotHeight-l,!0,0,0,1);!isNaN(e)&& -!isNaN(d)&&(f[b?"xAxis":"yAxis"].push({axis:a,min:O(e,d),max:t(e,d)}),m=!0)}}),m&&N(c,"selection",f,function(a){c.zoom(a)}));b.selectionMarker=b.selectionMarker.destroy()}if(c)M(d,{cursor:"auto"}),c.cancelClick=e,c.mouseIsDown=e=!1;Q(I,aa?"touchend":"mouseup",a)}var b=this,c=b.chart,d=c.container,e,f=b.zoomX&&!c.inverted||b.zoomY&&c.inverted,g=b.zoomY&&!c.inverted||b.zoomX&&c.inverted;b.hideTooltipOnMouseMove=function(a){Ub(a);b.chartPosition&&c.hoverSeries&&c.hoverSeries.isCartesian&&!c.isInsidePlot(a.pageX- -b.chartPosition.left-c.plotLeft,a.pageY-b.chartPosition.top-c.plotTop)&&b.resetTracker()};b.hideTooltipOnMouseLeave=function(){b.resetTracker();b.chartPosition=null};d.onmousedown=function(d){d=b.normalizeMouseEvent(d);!aa&&d.preventDefault&&d.preventDefault();c.mouseIsDown=!0;c.cancelClick=!1;c.mouseDownX=b.mouseDownX=d.chartX;b.mouseDownY=d.chartY;B(I,aa?"touchend":"mouseup",a)};var h=function(a){if(!a||!(a.touches&&a.touches.length>1)){a=b.normalizeMouseEvent(a);if(!aa)a.returnValue=!1;var d=a.chartX, -h=a.chartY,l=!c.isInsidePlot(d-c.plotLeft,h-c.plotTop);aa&&a.type==="touchstart"&&(G(a.target,"isTracker")?c.runTrackerClick||a.preventDefault():!c.runChartClick&&!l&&a.preventDefault());if(l)dc.plotLeft+c.plotWidth&&(d=c.plotLeft+c.plotWidth),hc.plotTop+c.plotHeight&&(h=c.plotTop+c.plotHeight);if(c.mouseIsDown&&a.type!=="touchstart"){if(e=Math.sqrt(Math.pow(b.mouseDownX-d,2)+Math.pow(b.mouseDownY-h,2)),e>10){a=c.isInsidePlot(b.mouseDownX-c.plotLeft, -b.mouseDownY-c.plotTop);if(c.hasCartesianSeries&&(b.zoomX||b.zoomY)&&a&&!b.selectionMarker)b.selectionMarker=c.renderer.rect(c.plotLeft,c.plotTop,f?1:c.plotWidth,g?1:c.plotHeight,0).attr({fill:b.options.chart.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();if(b.selectionMarker&&f){var m=d-b.mouseDownX;b.selectionMarker.attr({width:W(m),x:(m>0?0:m)+b.mouseDownX})}b.selectionMarker&&g&&(h-=b.mouseDownY,b.selectionMarker.attr({height:W(h),y:(h>0?0:h)+b.mouseDownY}));a&&!b.selectionMarker&& -b.options.chart.panning&&c.pan(d)}}else if(!l)b.onmousemove(a);return l||!c.hasCartesianSeries}};d.onmousemove=h;B(d,"mouseleave",b.hideTooltipOnMouseLeave);B(I,"mousemove",b.hideTooltipOnMouseMove);d.ontouchstart=function(a){if(b.zoomX||b.zoomY)d.onmousedown(a);h(a)};d.ontouchmove=h;d.ontouchend=function(){e&&b.resetTracker()};d.onclick=function(a){var d=c.hoverPoint,e,f,a=b.normalizeMouseEvent(a);a.cancelBubble=!0;if(!c.cancelClick)d&&(G(a.target,"isTracker")||G(a.target.parentNode,"isTracker"))? -(e=d.plotX,f=d.plotY,z(d,{pageX:b.chartPosition.left+c.plotLeft+(c.inverted?c.plotWidth-f:e),pageY:b.chartPosition.top+c.plotTop+(c.inverted?c.plotHeight-e:f)}),N(d.series,"click",z(a,{point:d})),d.firePointEvent("click",a)):(z(a,b.getMouseCoordinates(a)),c.isInsidePlot(a.chartX-c.plotLeft,a.chartY-c.plotTop)&&N(c,"click",a))}},destroy:function(){var a=this.chart,b=a.container;if(a.trackerGroup)a.trackerGroup=a.trackerGroup.destroy();Q(b,"mouseleave",this.hideTooltipOnMouseLeave);Q(I,"mousemove", -this.hideTooltipOnMouseMove);b.onclick=b.onmousedown=b.onmousemove=b.ontouchstart=b.ontouchend=b.ontouchmove=null;clearInterval(this.tooltipInterval)},init:function(a,b){if(!a.trackerGroup)a.trackerGroup=a.renderer.g("tracker").attr({zIndex:9}).add();if(b.enabled)a.tooltip=new wb(a,b),this.tooltipInterval=setInterval(function(){a.tooltip.tick()},32);this.setDOMEvents()}};xb.prototype={init:function(a){var b=this,c=b.options=a.options.legend;if(c.enabled){var d=c.itemStyle,e=p(c.padding,8),f=c.itemMarginTop|| -0;b.baseline=F(d.fontSize)+3+f;b.itemStyle=d;b.itemHiddenStyle=y(d,c.itemHiddenStyle);b.itemMarginTop=f;b.padding=e;b.initialItemX=e;b.initialItemY=e-5;b.maxItemWidth=0;b.chart=a;b.itemHeight=0;b.lastLineHeight=0;b.render();B(b.chart,"endResize",function(){b.positionCheckboxes()})}},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,g=b?a.color:g;d&&d.css({fill:c});e&&e.attr({stroke:g});f&&f.attr({stroke:g, -fill:g})},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl,d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Na(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(){var a= -this;n(a.allItems,function(b){var c=b.checkbox,d=a.group.alignAttr;c&&M(c,{left:d.translateX+b.legendItemWidth+c.x-20+"px",top:d.translateY+c.y+3+"px"})})},renderItem:function(a){var x;var b=this,c=b.chart,d=c.renderer,e=b.options,f=e.layout==="horizontal",g=e.symbolWidth,h=e.symbolPadding,k=b.itemStyle,i=b.itemHiddenStyle,j=b.padding,l=!e.rtl,m=e.width,o=e.itemMarginBottom||0,r=b.itemMarginTop,n=b.initialItemX,q=a.legendItem,p=a.series||a,s=p.options,w=s.showCheckbox;if(!q&&(a.legendGroup=d.g("legend-item").attr({zIndex:1}).add(b.scrollGroup), -p.drawLegendSymbol(b,a),a.legendItem=q=d.text(e.labelFormatter.call(a),l?g+h:-h,b.baseline,e.useHTML).css(y(a.visible?k:i)).attr({align:l?"left":"right",zIndex:2}).add(a.legendGroup),a.legendGroup.on("mouseover",function(){a.setState("hover");q.css(b.options.itemHoverStyle)}).on("mouseout",function(){q.css(a.visible?k:i);a.setState()}).on("click",function(b){var c=function(){a.setVisible()},b={browserEvent:b};a.firePointEvent?a.firePointEvent("legendItemClick",b,c):N(a,"legendItemClick",b,c)}),b.colorizeItem(a, -a.visible),s&&w))a.checkbox=V("input",{type:"checkbox",checked:a.selected,defaultChecked:a.selected},e.itemCheckboxStyle,c.container),B(a.checkbox,"click",function(b){N(a,"checkboxClick",{checked:b.target.checked},function(){a.select()})});d=q.getBBox();x=a.legendItemWidth=e.itemWidth||g+h+d.width+j+(w?20:0),e=x;b.itemHeight=g=d.height;if(f&&b.itemX-n+e>(m||c.chartWidth-2*j-n))b.itemX=n,b.itemY+=r+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=t(b.maxItemWidth,e);b.lastItemY=r+b.itemY+o;b.lastLineHeight= -t(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=r+g+o,b.lastLineHeight=g);b.offsetWidth=m||t(f?b.itemX-n:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,k=a.box,i=a.options,j=a.padding,l=i.borderWidth,m=i.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup), -a.clipRect=c.clipRect(0,0,9999,b.chartHeight),a.contentGroup.clip(a.clipRect);e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&(e=e.concat(a.legendItems||(b.legendType==="point"?a.data:a)))});ac(e,function(a,b){return(a.options.legendIndex||0)-(b.options.legendIndex||0)});i.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=i.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight;h=a.handleOverflow(h);if(l||m){g+=j;h+=j;if(k){if(g>0&&h>0)k[k.isNew? -"attr":"animate"](k.crisp(null,null,null,g,h)),k.isNew=!1}else a.box=k=c.rect(0,0,g,h,i.borderRadius,l||0).attr({stroke:i.borderColor,"stroke-width":l||0,fill:m||da}).add(d).shadow(i.shadow),k.isNew=!0;k[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(z({width:g,height:h},i),!0,b.spacingBox);b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign=== -"top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,k=e.navigation,i=p(k.animation,!0),j=k.arrowSize||12,l=this.nav;e.layout==="horizontal"&&(f/=2);g&&(f=O(f,g));if(a>f){this.clipHeight=c=f-20;this.pageCount=Ga(a/c);this.currentPage=p(this.currentPage,1);this.fullHeight=a;h.attr({height:c});if(!l)this.nav=l=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,j,j).on("click",function(){b.scroll(-1,i)}).add(l),this.pager=d.text("",15,10).css(k.style).add(l),this.down=d.symbol("triangle-down", -0,0,j,j).on("click",function(){b.scroll(1,i)}).add(l);b.scroll(0);a=f}else l&&(h.attr({height:c.chartHeight}),l.hide(),this.scrollGroup.attr({translateY:1}));return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,f=f.inactiveColor,h=this.pager,k=this.padding;d>c&&(d=c);if(d>0)b!==v&&Ea(b,this.chart),this.nav.attr({translateX:k,translateY:e+7,visibility:"visible"}),this.up.attr({fill:d===1?f:g}).css({cursor:d===1?"default": -"pointer"}),h.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?f:g}).css({cursor:d===c?"default":"pointer"}),this.scrollGroup.animate({translateY:-O(e*(d-1),this.fullHeight-e+k)+1}),h.attr({text:d+"/"+c}),this.currentPage=d}};cb.prototype={initSeries:function(a){var b=this.options.chart,b=new U[a.type||b.type||b.defaultSeriesType];b.init(this,a);return b},addSeries:function(a,b,c){var d=this;a&&(Ea(c,d),b=p(b,!0),N(d,"addSeries",{options:a},function(){d.initSeries(a); -d.isDirtyLegend=!0;b&&d.redraw()}))},isInsidePlot:function(a,b){return a>=0&&a<=this.plotWidth&&b>=0&&b<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.tracker,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,k=c.length,i=k,j=this.clipRect,l=this.renderer,m=l.isHidden();Ea(a,this);for(m&&this.cloneRenderTo();i--;)if(a=c[i],a.isDirty&&a.options.stacking){g= -!0;break}if(g)for(i=k;i--;)if(a=c[i],a.options.stacking)a.isDirty=!0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,N(a,"afterSetExtremes",a.getExtremes());if(a.isDirty||h||g)a.redraw(),h=!0})}h&&(this.drawChartBox(),j&&(Ra(j), -j.animate({width:this.plotSizeX,height:this.plotSizeY+1})));n(c,function(a){a.isDirty&&a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.resetTracker&&d.resetTracker(!0);l.draw();N(this,"redraw");m&&this.cloneRenderTo(!0)},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=V(oa,{className:"highcharts-loading"},z(d.style,{left:this.plotLeft+"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px",zIndex:10,display:da}),this.container), -this.loadingSpan=V("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)M(c,{opacity:0,display:""}),lb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&lb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){M(b,{display:da})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;dO(e.dataMin,e.min)&&c19?this.containerHeight: -400)},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Na(b),delete this.renderToClone):(c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),M(b,{position:"absolute",top:"-9999px",display:"block"}),I.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+yb++;if(ya(a))this.renderTo=a=I.getElementById(a);a||ub(13,!0);a.innerHTML=""; -a.offsetWidth||this.cloneRenderTo();this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=V(oa,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},z({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal"},b.style),this.renderToClone||a);this.renderer=b.forExport?new qa(a,c,d,!0):new Qa(a,c,d);ma&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart,b=a.spacingTop,c=a.spacingRight,d= -a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,k=this.optionsMarginRight,i=this.optionsMarginBottom,j=this.chartTitleOptions,l=this.chartSubtitleOptions,m=this.options.legend,o=p(m.margin,10),r=m.x,A=m.y,q=m.align,C=m.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!s(this.optionsMarginTop))if(l=t(this.title&&!j.floating&&!j.verticalAlign&&j.y||0,this.subtitle&&!l.floating&&!l.verticalAlign&&l.y||0))this.plotTop= -t(this.plotTop,l+p(j.margin,15)+b);if(f.display&&!m.floating)if(q==="right"){if(!s(k))this.marginRight=t(this.marginRight,f.legendWidth-r+o+c)}else if(q==="left"){if(!s(h))this.plotLeft=t(this.plotLeft,f.legendWidth+r+o+a)}else if(C==="top"){if(!s(g))this.plotTop=t(this.plotTop,f.legendHeight+A+o+b)}else if(C==="bottom"&&!s(i))this.marginBottom=t(this.marginBottom,f.legendHeight-A+o+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+=this.extraTopMargin); -this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});s(h)||(this.plotLeft+=e[3]);s(g)||(this.plotTop+=e[0]);s(i)||(this.marginBottom+=e[2]);s(k)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||eb(d,"width"),h=c.height||eb(d,"height"),a=a?a.target:Y;if(g&&h&&(a===Y||a===I)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),e=setTimeout(function(){b.resize(g,h,!1)},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart, -d=b.renderTo,e;B(Y,"resize",a);B(b,"destroy",function(){Q(Y,"resize",a)})},fireEndResize:function(){var a=this;a&&N(a,"endResize",null,function(){a.isResizing-=1})},resize:function(a,b,c){var d,e,f=this.title,g=this.subtitle;this.isResizing+=1;Ea(c,this);this.oldChartHeight=this.chartHeight;this.oldChartWidth=this.chartWidth;if(s(a))this.chartWidth=d=u(a);if(s(b))this.chartHeight=e=u(b);M(this.container,{width:d+"px",height:e+"px"});this.renderer.setSize(d,e,c);this.plotWidth=d-this.plotLeft-this.marginRight; -this.plotHeight=e-this.plotTop-this.marginBottom;this.maxTicks=null;n(this.axes,function(a){a.isDirty=!0;a.setScale()});n(this.series,function(a){a.isDirty=!0});this.isDirtyBox=this.isDirtyLegend=!0;this.getMargins();a=this.spacingBox;f&&f.align(null,null,a);g&&g.align(null,null,a);this.redraw(c);this.oldChartHeight=null;N(this,"resize");$a===!1?this.fireEndResize():setTimeout(this.fireEndResize,$a&&$a.duration||500)},setChartSize:function(){var a=this.inverted,b=this.chartWidth,c=this.chartHeight, -d=this.options.chart,e=d.spacingTop,f=d.spacingRight,g=d.spacingBottom,d=d.spacingLeft;this.plotLeft=u(this.plotLeft);this.plotTop=u(this.plotTop);this.plotWidth=u(b-this.plotLeft-this.marginRight);this.plotHeight=u(c-this.plotTop-this.marginBottom);this.plotSizeX=a?this.plotHeight:this.plotWidth;this.plotSizeY=a?this.plotWidth:this.plotHeight;this.spacingBox={x:d,y:e,width:b-d-f,height:c-e-g};n(this.axes,function(a){a.setAxisSize();a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart, -b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=p(this.optionsMarginTop,a.spacingTop);this.marginRight=p(this.optionsMarginRight,b);this.marginBottom=p(this.optionsMarginBottom,c);this.plotLeft=p(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground,g=this.plotBorder,h=this.plotBGImage,k=a.borderWidth||0,i=a.backgroundColor,j=a.plotBackgroundColor, -l=a.plotBackgroundImage,m,o={x:this.plotLeft,y:this.plotTop,width:this.plotWidth,height:this.plotHeight};m=k+(a.shadow?8:0);if(k||i)if(e)e.animate(e.crisp(null,null,null,c-m,d-m));else{e={fill:i||da};if(k)e.stroke=a.borderColor,e["stroke-width"]=k;this.chartBackground=b.rect(m/2,m/2,c-m,d-m,a.borderRadius,k).attr(e).add().shadow(a.shadow)}if(j)f?f.animate(o):this.plotBackground=b.rect(this.plotLeft,this.plotTop,this.plotWidth,this.plotHeight,0).attr({fill:j}).add().shadow(a.plotShadow);if(l)h?h.animate(o): -this.plotBGImage=b.image(l,this.plotLeft,this.plotTop,this.plotWidth,this.plotHeight).add();if(a.plotBorderWidth)g?g.animate(g.crisp(null,this.plotLeft,this.plotTop,this.plotWidth,this.plotHeight)):this.plotBorder=b.rect(this.plotLeft,this.plotTop,this.plotWidth,this.plotHeight,0,a.plotBorderWidth).attr({stroke:a.plotBorderColor,"stroke-width":a.plotBorderWidth,zIndex:4}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular", -"polar"],function(g){c=U[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=U[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,d=d.credits,f;a.setTitle();a.legend=new xb(a);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()}); -if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(){var b=z(e.style,this.style),d=F(b.left)+a.plotLeft,f=F(b.top)+a.plotTop+12;delete b.left;delete b.top;c.text(this.html,d,f).attr({zIndex:2}).css(b).add()});if(d.enabled&&!a.credits)f=d.href,a.credits=c.text(d.text,0,0).on("click",function(){if(f)location.href=f}).attr({align:d.position.align,zIndex:8}).css(d.style).add().align(d.position); -a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;if(a!==null){N(a,"destroy");Q(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,tracker,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&(a[b]=c.destroy())});if(d)d.innerHTML="",Q(d),f&&Na(d),d=null;for(e in a)delete a[e]; -a=a.options=null}},firstRender:function(){var a=this,b=a.options,c=a.callback;if(!Pa&&Y==Y.top&&I.readyState!=="complete"||ma&&!Y.canvg)ma?Xb.push(function(){a.firstRender()},b.global.canvasToolsURL):I.attachEvent("onreadystatechange",function(){I.detachEvent("onreadystatechange",a.firstRender);I.readyState==="complete"&&a.firstRender()});else{a.getContainer();N(a,"init");if(Highcharts.RangeSelector&&b.rangeSelector.enabled)a.rangeSelector=new Highcharts.RangeSelector(a);a.resetMargins();a.setChartSize(); -a.propFromSeries();a.getAxes();n(b.series||[],function(b){a.initSeries(b)});if(Highcharts.Scroller&&(b.navigator.enabled||b.scrollbar.enabled))a.scroller=new Highcharts.Scroller(a);a.tracker=new Kb(a,b);a.render();a.renderer.draw();c&&c.apply(a,[a]);n(a.callbacks,function(b){b.apply(a,[a])});a.cloneRenderTo(!0);N(a,"load")}},init:function(a){var b=this.options.chart,c;b.reflow!==!1&&B(this,"load",this.initReflow);if(a)for(c in a)B(this,c,a[c]);this.xAxis=[];this.yAxis=[];this.animation=ma?!1:p(b.animation, -!0);this.setSize=this.resize;this.pointCount=0;this.counters=new Hb;this.firstRender()}};cb.prototype.callbacks=[];var xa=function(){};xa.prototype={init:function(a,b,c){var d=a.chart.counters;this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint){b=a.chart.options.colors;if(!this.options)this.options={};this.color=this.options.color=this.color||b[d.color++];d.wrapColor(b.length)}a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=typeof a;this.config= -a;if(d==="number"||a===null)this.y=a;else if(typeof a[0]==="number")this.x=a[0],this.y=a[1];else if(d==="object"&&typeof a.length!=="number"){if(z(this,a),this.options=a,a.dataLabels)c._hasPointLabels=!0}else if(typeof a[0]==="string")this.name=a[0],this.y=a[1];if(this.x===v)this.x=b===v?c.autoIncrement():b},destroy:function(){var a=this.series.chart,b=a.hoverPoints,c;a.pointCount--;if(b&&(this.setState(),Ka(b,this),!b.length))a.hoverPoints=null;if(this===a.hoverPoint)this.onMouseOut();if(this.graphic|| -this.dataLabel)Q(this),this.destroyElements();this.legendItem&&a.legend.destroyItem(this);for(c in this)this[c]=null},destroyElements:function(){for(var a="graphic,tracker,dataLabel,group,connector,shadowGroup".split(","),b,c=6;c--;)b=a[c],this[b]&&(this[b]=this[b].destroy())},getLabelConfig:function(){return{x:this.category,y:this.y,key:this.name||this.category,series:this.series,point:this,percentage:this.percentage,total:this.total||this.stackTotal}},select:function(a,b){var c=this,d=c.series.chart, -a=p(a,!c.selected);c.firePointEvent(a?"select":"unselect",{accumulate:b},function(){c.selected=a;c.setState(a&&"select");b||n(d.getSelectedPoints(),function(a){if(a.selected&&a!==c)a.selected=!1,a.setState(""),a.firePointEvent("unselect")})})},onMouseOver:function(){var a=this.series,b=a.chart,c=b.tooltip,d=b.hoverPoint;if(d&&d!==this)d.onMouseOut();this.firePointEvent("mouseOver");c&&(!c.shared||a.noSharedTooltip)&&c.refresh(this);this.setState("hover");b.hoverPoint=this},onMouseOut:function(){this.firePointEvent("mouseOut"); -this.setState();this.series.chart.hoverPoint=null},tooltipFormatter:function(a){var b=this.series,c=b.tooltipOptions,d=a.match(/\{(series|point)\.[a-zA-Z]+\}/g),e=/[{\.}]/,f,g,h,k,i={y:0,open:0,high:0,low:0,close:0,percentage:1,total:1};c.valuePrefix=c.valuePrefix||c.yPrefix;c.valueDecimals=c.valueDecimals||c.yDecimals;c.valueSuffix=c.valueSuffix||c.ySuffix;for(k in d)g=d[k],ya(g)&&g!==a&&(h=(" "+g).split(e),f={point:this,series:b}[h[1]],h=h[2],f===this&&i.hasOwnProperty(h)?(f=i[h]?h:"value",f=(c[f+ -"Prefix"]||"")+Ya(this[h],p(c[f+"Decimals"],-1))+(c[f+"Suffix"]||"")):f=f[h],a=a.replace(g,f));return a},update:function(a,b,c){var d=this,e=d.series,f=d.graphic,g,h=e.data,k=h.length,i=e.chart,b=p(b,!0);d.firePointEvent("update",{options:a},function(){d.applyOptions(a);ia(a)&&(e.getAttribs(),f&&f.attr(d.pointAttr[e.state]));for(g=0;ga+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions,d=a.data;a.data=null;c=y(c[this.type],c.series,a);c.data=a.data=d;this.tooltipOptions=y(b.tooltip,c.tooltip);return c},getColor:function(){var a=this.options,b=this.chart.options.colors,c=this.chart.counters;this.color=a.color||!a.colorByPoint&&b[c.color++]||"gray"; -c.wrapColor(b.length)},getSymbol:function(){var a=this.options.marker,b=this.chart,c=b.options.symbols,b=b.counters;this.symbol=a.symbol||c[b.symbol++];if(/^url/.test(this.symbol))a.radius=0;b.wrapSymbol(c.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&& -c.enabled)b=c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).attr(this.pointAttr[""]).add(f)},addPoint:function(a,b,c,d){var e=this.data,f=this.graph,g=this.area,h=this.chart,k=this.xData,i=this.yData,j=f&&f.shift||0,l=this.options.data;Ea(d,h);if(f&&c)f.shift=j+1;if(g){if(c)g.shift=j+1;g.isArea=!0}b=p(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);k.push(d.x);i.push(this.valueCount===4?[d.open,d.high,d.low,d.close]:d.y);l.push(a);c&&(e[0]&&e[0].remove? -e[0].remove(!1):(e.shift(),k.shift(),i.shift(),l.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&h.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.initialColor,f=this.chart,g=null,h=this.xAxis,k=this.pointClass.prototype;this.xIncrement=null;this.pointRange=h&&h.categories&&1||d.pointRange;if(s(e))f.counters.color=e;var i=[],j=[],l=a?a.length:[],m=this.valueCount;if(l>(d.turboThreshold||1E3)){for(e=0;g===null&&ej||this.forceCrop))if(a=k.getExtremes(),k=a.min,j= -a.max,b[d-1]j)b=[],c=[];else if(b[0]j){for(a=0;a=k){e=t(0,a-1);break}for(;aj){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===v||d=0&&d<=e;)h[d++]=f}this.tooltipPoints=h}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=this.xAxis,e=d&&d.options.type==="datetime",f;if(e&&!c)for(f in E)if(E[f]>=d.closestPointRange){c=b.dateTimeLabelFormats[f];break}return b.headerFormat.replace("{point.key}",e?ua(c,a):a).replace("{series.name}",this.name).replace("{series.color}",this.color)},onMouseOver:function(){var a= -this.chart,b=a.hoverSeries;if(aa||!a.mouseIsDown){if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&&N(this,"mouseOver");this.setState("hover");a.hoverSeries=this}},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&N(this,"mouseOut");c&&!a.stickyTracking&&!c.shared&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this.chart,c=this.clipRect,d=this.options.animation;d&&!ia(d)&&(d={});if(a){if(!c.isAnimating)c.attr("width", -0),c.isAnimating=!0}else c.animate({width:b.plotSizeX},d),this.animate=null},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,k,i,j;if(this.options.marker.enabled)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,j=g.graphic,e!==v&&!isNaN(e))if(a=g.pointAttr[g.selected?"select":""],h=a.r,k=p(g.marker&&g.marker.symbol,this.symbol),i=k.indexOf("url")===0,j)j.animate(z({x:d-h,y:e-h},j.symbolName?{width:2*h,height:2*h}:{}));else if(h>0||i)g.graphic=c.renderer.symbol(k,d-h,e-h,2*h,2*h).attr(a).add(this.group)}, -convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=S[a.type].marker?a.options.marker:a.options,c=b.states,d=c.hover,e,f=a.color,g={stroke:f,fill:f},h=a.points||[],k=[],i,j=a.pointAttrToOptions,l;a.options.marker?(d.radius=d.radius||b.radius+2,d.lineWidth=d.lineWidth||b.lineWidth+1):d.color=d.color||wa(d.color||f).brighten(d.brightness).get();k[""]=a.convertAttribs(b, -g);n(["hover","select"],function(b){k[b]=a.convertAttribs(c[b],k[""])});a.pointAttr=k;for(f=h.length;f--;){g=h[f];if((b=g.options&&g.options.marker||g.options)&&b.enabled===!1)b.radius=0;e=!1;if(g.options)for(l in j)s(b[j[l]])&&(e=!0);if(e){i=[];c=b.states||{};e=c.hover=c.hover||{};if(!a.options.marker)e.color=wa(e.color||g.options.color).brighten(e.brightness||d.brightness).get();i[""]=a.convertAttribs(b,k[""]);i.hover=a.convertAttribs(c.hover,k.hover,i[""]);i.select=a.convertAttribs(c.select,k.select, -i[""])}else i=k;g.pointAttr=i}},destroy:function(){var a=this,b=a.chart,c=a.clipRect,d=/AppleWebKit\/533/.test(Ia),e,f,g=a.data||[],h,k,i;N(a,"destroy");Q(a);n(["xAxis","yAxis"],function(b){if(i=a[b])Ka(i.series,a),i.isDirty=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(f=g.length;f--;)(h=g[f])&&h.destroy&&h.destroy();a.points=null;if(c&&c!==b.clipRect)a.clipRect=c.destroy();n("area,graph,dataLabelsGroup,group,tracker,trackerGroup".split(","),function(b){a[b]&&(e=d&&b==="group"?"hide":"destroy", -a[b][e]())});if(b.hoverSeries===a)b.hoverSeries=null;Ka(b.series,a);for(k in a)delete a[k]},drawDataLabels:function(){var a=this,b=a.options,c=b.dataLabels;if(c.enabled||a._hasPointLabels){var d,e,f=a.points,g,h,k,i=a.dataLabelsGroup,j=a.chart,l=a.xAxis,l=l?l.left:j.plotLeft,m=a.yAxis,m=m?m.top:j.plotTop,o=j.renderer,r=j.inverted,A=a.type,q=b.stacking,C=A==="column"||A==="bar",J=c.verticalAlign===null,w=c.y===null,x=o.fontMetrics(c.style.fontSize),D=x.h,K=x.b,H,t;C&&(x={top:K,middle:K-D/2,bottom:-D+ -K},q?(J&&(c=y(c,{verticalAlign:"middle"})),w&&(c=y(c,{y:x[c.verticalAlign]}))):J?c=y(c,{verticalAlign:"top"}):w&&(c=y(c,{y:x[c.verticalAlign]})));i?i.translate(l,m):i=a.dataLabelsGroup=o.g("data-labels").attr({visibility:a.visible?"visible":"hidden",zIndex:6}).translate(l,m).add();h=c;n(f,function(f){H=f.dataLabel;c=h;(g=f.options)&&g.dataLabels&&(c=y(c,g.dataLabels));if(t=c.enabled){var l=f.barX&&f.barX+f.barW/2||p(f.plotX,-999),m=p(f.plotY,-999),n=c.y===null?f.y>=b.threshold?-D+K:K:c.y;d=(r?j.plotWidth- -m:l)+c.x;e=u((r?j.plotHeight-l:m)+n)}if(H&&a.isCartesian&&(!j.isInsidePlot(d,e)||!t))f.dataLabel=H.destroy();else if(t){var l=c.align,w;k=c.formatter.call(f.getLabelConfig(),c);A==="column"&&(d+={left:-1,right:1}[l]*f.barW/2||0);!q&&r&&f.y<0&&(l="right",d-=10);c.style.color=p(c.color,c.style.color,a.color,"black");if(H)H.attr({text:k}).animate({x:d,y:e});else if(s(k)){l={align:l,fill:c.backgroundColor,stroke:c.borderColor,"stroke-width":c.borderWidth,r:c.borderRadius||0,rotation:c.rotation,padding:c.padding, -zIndex:1};for(w in l)l[w]===v&&delete l[w];H=f.dataLabel=o[c.rotation?"text":"label"](k,d,e,null,null,null,c.useHTML,!0).attr(l).css(c.style).add(i).shadow(c.shadow)}if(C&&b.stacking&&H)w=f.barX,l=f.barY,m=f.barW,f=f.barH,H.align(c,null,{x:r?j.plotWidth-l-f:w,y:r?j.plotHeight-w-m:l,width:r?f:m,height:r?m:f})}})}},getSegmentPath:function(a){var b=this,c=[];n(a,function(d,e){b.getPointSpline?c.push.apply(c,b.getPointSpline(a,d,e)):(c.push(e?"L":"M"),e&&b.options.step&&c.push(d.plotX,a[e-1].plotY),c.push(d.plotX, -d.plotY))});return c},drawGraph:function(){var a=this,b=a.options,c=a.graph,d=[],e=a.group,f=b.lineColor||a.color,g=b.lineWidth,h=b.dashStyle,k,i=a.chart.renderer,j=[];n(a.segments,function(b){k=a.getSegmentPath(b);b.length>1?d=d.concat(k):j.push(b[0])});a.graphPath=d;a.singlePoints=j;if(c)Ra(c),c.animate({d:d});else if(g){c={stroke:f,"stroke-width":g};if(h)c.dashstyle=h;a.graph=i.path(d).attr(c).add(e).shadow(b.shadow)}},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len}; -c.attr(a).invert();d&&d.attr(a).invert()}var b=this,c=b.group,d=b.trackerGroup,e=b.chart;B(e,"resize",a);B(b,"destroy",function(){Q(e,"resize",a)});a();b.invertGroups=a},createGroup:function(){var a=this.chart;(this.group=a.renderer.g("series")).attr({visibility:this.visible?"visible":"hidden",zIndex:this.options.zIndex}).translate(this.xAxis.left,this.yAxis.top).add(a.seriesGroup);this.createGroup=Rb},render:function(){var a=this,b=a.chart,c,d=a.options,e=d.clip!==!1,f=d.animation,f=(d=f&&a.animate)? -f&&f.duration||500:0,g=a.clipRect,h=b.renderer;if(!g&&(g=a.clipRect=!b.hasRendered&&b.clipRect?b.clipRect:h.clipRect(0,0,b.plotSizeX,b.plotSizeY+1),!b.clipRect))b.clipRect=g;a.createGroup();c=a.group;a.drawDataLabels();d&&a.animate(!0);a.getAttribs();a.drawGraph&&a.drawGraph();a.drawPoints();a.options.enableMouseTracking!==!1&&a.drawTracker();b.inverted&&a.invertGroups();e&&!a.hasRendered&&(c.clip(g),a.trackerGroup&&a.trackerGroup.clip(b.clipRect));d&&a.animate();setTimeout(function(){g.isAnimating= -!1;if((c=a.group)&&g!==b.clipRect&&g.renderer){if(e)c.clip(a.clipRect=b.clipRect);g.destroy()}},f);a.isDirty=a.isDirtyData=!1;a.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:this.xAxis.left,translateY:this.yAxis.top}));this.translate();this.setTooltipPoints(!0);this.render();b&&N(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=b.states,b=b.lineWidth, -a=a||"";if(this.state!==a)this.state=a,d[a]&&d[a].enabled===!1||(a&&(b=d[a].lineWidth||b+1),c&&!c.dashstyle&&c.attr({"stroke-width":b},a?0:500))},setVisible:function(a,b){var c=this.chart,d=this.legendItem,e=this.group,f=this.tracker,g=this.dataLabelsGroup,h,k=this.points,i=c.options.chart.ignoreHiddenSeries;h=this.visible;h=(this.visible=a=a===v?!h:a)?"show":"hide";if(e)e[h]();if(f)f[h]();else if(k)for(e=k.length;e--;)if(f=k[e],f.tracker)f.tracker[h]();if(g)g[h]();d&&c.legend.colorizeItem(this,a); -this.isDirty=!0;this.options.stacking&&n(c.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});if(i)c.isDirtyBox=!0;b!==!1&&c.redraw();N(this,h)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===v?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;N(this,a?"select":"unselect")},drawTrackerGroup:function(){var a=this.trackerGroup,b=this.chart;if(this.isCartesian){if(!a)this.trackerGroup=a=b.renderer.g().attr({zIndex:this.options.zIndex|| -1}).add(b.trackerGroup);a.translate(this.xAxis.left,this.yAxis.top)}return a},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.renderer,h=f.options.tooltip.snap,k=a.tracker,i=b.cursor,i=i&&{cursor:i},j=a.singlePoints,l=a.drawTrackerGroup(),m;if(e&&!c)for(m=e+1;m--;)d[m]==="M"&&d.splice(m+1,0,d[m+1]-h,d[m+2],"L"),(m&&d[m]==="M"||m===e)&&d.splice(m,0,"L",d[m-2]+h,d[m-1]);for(m=0;m=0;d--)da&&k>e?(k=t(a,e),j=2*e-k):kg&&j>e?(j=t(g,e),k=2*e-j):jx?g-x:w-(f<=w?x:0));z(c,{barX:h,barY:i,barW:C,barH:k,pointWidth:q}); -c.shapeType="rect";c.shapeArgs=f=b.renderer.Element.prototype.crisp.call(0,e,h,i,C,k);e%2&&(f.y-=1,f.height+=1);c.trackerArgs=W(k)<3&&y(c.shapeArgs,{height:6,y:i-3})})},getSymbol:function(){},drawLegendSymbol:L.prototype.drawLegendSymbol,drawGraph:function(){},drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d,e;n(a.points,function(f){var g=f.plotY;if(g!==v&&!isNaN(g)&&f.y!==null)d=f.graphic,e=f.shapeArgs,d?(Ra(d),d.animate(y(e))):f.graphic=d=c[f.shapeType](e).attr(f.pointAttr[f.selected? -"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius)})},drawTracker:function(){var a=this,b=a.chart,c=b.renderer,d,e,f=+new Date,g=a.options,h=g.cursor,k=h&&{cursor:h},i=a.drawTrackerGroup(),j,l,m;n(a.points,function(h){e=h.tracker;d=h.trackerArgs||h.shapeArgs;l=h.plotY;m=!a.isCartesian||l!==v&&!isNaN(l);delete d.strokeWidth;if(h.y!==null&&m)e?e.attr(d):h.tracker=c[h.shapeType](d).attr({isTracker:f,fill:zb,visibility:a.visible?"visible":"hidden"}).on(aa?"touchstart":"mouseover", -function(c){j=c.relatedTarget||c.fromElement;if(b.hoverSeries!==a&&G(j,"isTracker")!==f)a.onMouseOver();h.onMouseOver()}).on("mouseout",function(b){if(!g.stickyTracking&&(j=b.relatedTarget||b.toElement,G(j,"isTracker")!==f))a.onMouseOut()}).css(k).add(h.group||i)})},animate:function(a){var b=this,c=b.points,d=b.options;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs,g=b.yAxis,h=d.threshold;c&&(c.attr({height:0,y:s(h)?g.getThreshold(h):g.translate(g.getExtremes().min,0,1,0,1)}),c.animate({height:a.height, -y:a.y},d.animation))}),b.animate=null},remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});Z.prototype.remove.apply(a,arguments)}});U.column=ea;S.bar=y(S.column,{dataLabels:{align:"left",x:5,y:null,verticalAlign:"middle"}});Ta=ca(ea,{type:"bar",inverted:!0});U.bar=Ta;S.scatter=y(P,{lineWidth:0,states:{hover:{lineWidth:0}},tooltip:{headerFormat:'{series.name}
    ',pointFormat:"x: {point.x}
    y: {point.y}
    "}}); -Ta=ca(Z,{type:"scatter",sorted:!1,translate:function(){var a=this;Z.prototype.translate.apply(a);n(a.points,function(b){b.shapeType="circle";b.shapeArgs={x:b.plotX,y:b.plotY,r:a.chart.options.tooltip.snap}})},drawTracker:function(){for(var a=this,b=a.options.cursor,b=b&&{cursor:b},c=a.points,d=c.length,e;d--;)if(e=c[d].graphic)e.element._i=d;a._hasTracking?a._hasTracking=!0:a.group.attr({isTracker:!0}).on(aa?"touchstart":"mouseover",function(b){a.onMouseOver();if(b.target._i!==v)c[b.target._i].onMouseOver()}).on("mouseout", -function(){if(!a.options.stickyTracking)a.onMouseOut()}).css(b)}});U.scatter=Ta;S.pie=y(P,{borderColor:"#FFFFFF",borderWidth:1,center:["50%","50%"],colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name},y:5},legendType:"point",marker:null,size:"75%",showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}}});P={type:"pie",isCartesian:!1,pointClass:ca(xa,{init:function(){xa.prototype.init.apply(this,arguments);var a=this,b;z(a,{visible:a.visible!== -!1,name:p(a.name,"Slice")});b=function(){a.slice()};B(a,"select",b);B(a,"unselect",b);return a},setVisible:function(a){var b=this.series.chart,c=this.tracker,d=this.dataLabel,e=this.connector,f=this.shadowGroup,g;g=(this.visible=a=a===v?!this.visible:a)?"show":"hide";this.group[g]();if(c)c[g]();if(d)d[g]();if(e)e[g]();if(f)f[g]();this.legendItem&&b.legend.colorizeItem(this,a)},slice:function(a,b,c){var d=this.series.chart,e=this.slicedTranslation;Ea(c,d);p(b,!0);a=this.sliced=s(a)?a:!this.sliced; -a={translateX:a?e[0]:d.plotLeft,translateY:a?e[1]:d.plotTop};this.group.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth",fill:"color"},getColor:function(){this.initialColor=this.chart.counters.color},animate:function(){var a=this;n(a.points,function(b){var c=b.graphic,b=b.shapeArgs,d=-Ha/2;c&&(c.attr({r:0,start:d,end:d}),c.animate({r:b.r,start:b.start,end:b.end},a.options.animation))});a.animate=null},setData:function(a, -b){Z.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();p(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=b.plotWidth,d=b.plotHeight,a=a.center.concat([a.size,a.innerSize||0]),e=O(c,d),f;return va(a,function(a,b){return(f=/%$/.test(a))?[c,d,e,e][b]*F(a)/100:a})},translate:function(){this.generatePoints();var a=0,b=-0.25,c=this.options,d=c.slicedOffset,e=d+c.borderWidth,f,g=this.chart,h,k,i,j=this.points,l=2*Ha,m,o,r,p=c.dataLabels.distance; -this.center=f=this.getCenter();this.getX=function(a,b){i=R.asin((a-f[1])/(f[2]/2+p));return f[0]+(b?-1:1)*ga(i)*(f[2]/2+p)};n(j,function(b){a+=b.y});n(j,function(c){m=a?c.y/a:0;h=u(b*l*1E3)/1E3;b+=m;k=u(b*l*1E3)/1E3;c.shapeType="arc";c.shapeArgs={x:f[0],y:f[1],r:f[2]/2,innerR:f[3]/2,start:h,end:k};i=(k+h)/2;c.slicedTranslation=va([ga(i)*d+g.plotLeft,ka(i)*d+g.plotTop],u);o=ga(i)*f[2]/2;r=ka(i)*f[2]/2;c.tooltipPos=[f[0]+o*0.7,f[1]+r*0.7];c.labelPos=[f[0]+o+ga(i)*p,f[1]+r+ka(i)*p,f[0]+o+ga(i)*e,f[1]+ -r+ka(i)*e,f[0]+o,f[1]+r,p<0?"center":i0,r=[[],[]],A,q,C,s,w=2,x;if(d.enabled){Z.prototype.drawDataLabels.apply(this);n(a,function(a){a.dataLabel&&r[a.labelPos[7]0){for(x=m-l-i;x<=m+l+i;x+=a)D.push(x);C=D.length;if(v>C){h=[].concat(H);h.sort(s);for(x=v;x--;)h[x].rank=x;for(x= -v;x--;)H[x].rank>=C&&H.splice(x,1);v=H.length}for(x=0;x0){if(q=u.pop(),t=q.i,q=q.y,A>q&&D[t+1]!==null||A=c[1]||q===k;)if(i=c.shift(),j=p(j,m,o,n),j!==v&&(g.push(i),h.push(j)),j=[],m=[],o=[],n=[],q===k)break;if(q===k)break;i=l?b[q]:null;if(d==="ohlc"){i=this.cropStart+q;var t=e&&e[i]||this.pointClass.prototype.applyOptions.apply({},[f[i]]);i=t.open;var s=t.high,w=t.low,t=t.close;if(typeof i==="number")j.push(i);else if(i===null)j.hasNulls=!0;if(typeof s==="number")m.push(s);else if(s===null)m.hasNulls= -!0;if(typeof w==="number")o.push(w);else if(w===null)o.hasNulls=!0;if(typeof t==="number")n.push(t);else if(t===null)n.hasNulls=!0}else if(typeof i==="number")j.push(i);else if(i===null)j.hasNulls=!0}return[g,h]};X.processData=function(){var a=this.options,b=a.dataGrouping,c=b&&b.enabled,d;this.forceCrop=c;if(dc.apply(this,arguments)!==!1&&c){this.destroyGroupedData();var e;e=this.chart;var c=this.processedXData,f=this.processedYData,g=e.plotSizeX,h=this.xAxis,k=p(h.groupPixelWidth,b.groupPixelWidth), -i=c.length,j=e.series,l=this.pointRange;if(!h.groupPixelWidth){for(e=j.length;e--;)j[e].xAxis===h&&j[e].options.dataGrouping&&(k=t(k,j[e].options.dataGrouping.groupPixelWidth));h.groupPixelWidth=k}if(i>g/k||i&&b.forced){d=!0;this.points=null;e=h.getExtremes();i=e.min;j=e.max;e=h.getGroupIntervalFactor&&h.getGroupIntervalFactor(i,j,c)||1;g=k*(j-i)/g*e;h=(h.getNonLinearTimeTicks||gb)(Cb(g,b.units||Yb),i,j,null,c,this.closestPointRange);f=X.groupData.apply(this,[c,f,h,b.approximation]);c=f[0];f=f[1]; -if(b.smoothed){e=c.length-1;for(c[e]=j;e--&&e>0;)c[e]+=g/2;c[0]=i}this.currentDataGrouping=h.info;if(a.pointRange===null)this.pointRange=h.info.totalRange;this.closestPointRange=h.info.totalRange;this.processedXData=c;this.processedYData=f}else this.currentDataGrouping=null,this.pointRange=l;this.hasGroupedData=d}};X.destroyGroupedData=function(){var a=this.groupedData;n(a||[],function(b,c){b&&(a[c]=b.destroy?b.destroy():null)});this.groupedData=null};X.generatePoints=function(){ec.apply(this);this.destroyGroupedData(); -this.groupedData=this.hasGroupedData?this.points:null};X.tooltipHeaderFormatter=function(a){var b=this.tooltipOptions,c=this.options.dataGrouping,d=b.xDateFormat,e,f=this.xAxis,g,h;if(f&&f.options.type==="datetime"&&c){g=this.currentDataGrouping;c=c.dateTimeLabelFormats;if(g)f=c[g.unitName],g.count===1?d=f[0]:(d=f[1],e=f[2]);else if(!d)for(h in E)if(E[h]>=f.closestPointRange){d=c[h][0];break}d=ua(d,a);e&&(d+=ua(e,a+g.totalRange-1));a=b.headerFormat.replace("{point.key}",d)}else a=gc.apply(this,[a]); -return a};X.destroy=function(){for(var a=this.groupedData||[],b=a.length;b--;)a[b]&&a[b].destroy();fc.apply(this)};S.line.dataGrouping=S.spline.dataGrouping=S.area.dataGrouping=S.areaspline.dataGrouping=P;S.column.dataGrouping=y(P,{approximation:"sum",groupPixelWidth:10});S.ohlc=y(S.column,{lineWidth:1,dataGrouping:{approximation:"ohlc",enabled:!0,groupPixelWidth:5},tooltip:{pointFormat:'{series.name}
    Open: {point.open}
    High: {point.high}
    Low: {point.low}
    Close: {point.close}
    '}, -states:{hover:{lineWidth:3}},threshold:null});var P=ca(xa,{applyOptions:function(a){var b=this.series,c=0;if(typeof a==="object"&&typeof a.length!=="number")z(this,a),this.options=a;else if(a.length){if(a.length===5){if(typeof a[0]==="string")this.name=a[0];else if(typeof a[0]==="number")this.x=a[0];c++}this.open=a[c++];this.high=a[c++];this.low=a[c++];this.close=a[c++]}this.y=this.high;if(this.x===v&&b)this.x=b.autoIncrement();return this},tooltipFormatter:function(){var a=this.series;return['',this.name||a.name,"
    Open: ",this.open,"
    High: ",this.high,"
    Low: ",this.low,"
    Close: ",this.close,"
    "].join("")},toYData:function(){return[this.open,this.high,this.low,this.close]}}),Bb=ca(U.column,{type:"ohlc",valueCount:4,pointClass:P,pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},translate:function(){var a=this.yAxis;U.column.prototype.translate.apply(this);n(this.points,function(b){if(b.open!==null)b.plotOpen=a.translate(b.open, -0,1,0,1);if(b.close!==null)b.plotClose=a.translate(b.close,0,1,0,1)})},drawPoints:function(){var a=this,b=a.chart,c,d,e,f,g,h,k,i;n(a.points,function(j){if(j.plotY!==v)k=j.graphic,c=j.pointAttr[j.selected?"selected":""],f=c["stroke-width"]%2/2,i=u(j.plotX)+f,g=u(j.barW/2),h=["M",i,u(j.yBottom),"L",i,u(j.plotY)],j.open!==null&&(d=u(j.plotOpen)+f,h.push("M",i,d,"L",i-g,d)),j.close!==null&&(e=u(j.plotClose)+f,h.push("M",i,e,"L",i+g,e)),k?k.animate({d:h}):j.graphic=b.renderer.path(h).attr(c).add(a.group)})}, -animate:null});U.ohlc=Bb;S.candlestick=y(S.column,{dataGrouping:{approximation:"ohlc",enabled:!0},lineColor:"black",lineWidth:1,states:{hover:{lineWidth:2}},tooltip:S.ohlc.tooltip,threshold:null,upColor:"white"});P=ca(Bb,{type:"candlestick",pointAttrToOptions:{fill:"color",stroke:"lineColor","stroke-width":"lineWidth"},getAttribs:function(){Bb.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,a=a.upColor,c=y(this.pointAttr);c[""].fill=a;c.hover.fill=b.hover.upColor||a;c.select.fill= -b.select.upColor||a;n(this.points,function(a){if(a.open=k.min&&c.x<=k.max?c.plotY=a.plotHeight: -c.shapeArgs={};if((e=b[d-1])&&e.plotX===c.plotX){if(e.stackIndex===v)e.stackIndex=0;c.stackIndex=e.stackIndex+1}})},drawPoints:function(){var a,b=this.points,c=this.chart.renderer,d,e,f=this.options,g=f.y,h=f.shape,k,i,j,l,m=f.lineWidth%2/2,o;for(j=b.length;j--;)if(l=b[j],d=l.plotX+m,i=l.stackIndex,e=l.plotY,e!==v&&(e=l.plotY+g+m-(i!==v&&i*f.stackDistance)),k=i?v:l.plotX+m,o=i?v:l.plotY,i=l.graphic,a=l.tracker,e!==v)a=l.pointAttr[l.selected?"select":""],i?i.attr({x:d,y:e,r:a.r,anchorX:k,anchorY:o}): -i=l.graphic=c.label(l.options.title||f.title||"A",d,e,h,k,o).css(y(f.style,l.style)).attr(a).attr({align:h==="flag"?"left":"center",width:f.width,height:f.height}).add(this.group).shadow(f.shadow),k=i.box,i=k.getBBox(),l.shapeArgs=z(i,{x:d-(h==="flag"?0:k.attr("width")/2),y:e});else if(i)l.graphic=i.destroy(),a&&a.attr("y",-9999)},drawTracker:function(){U.column.prototype.drawTracker.apply(this);n(this.points,function(a){B(a.tracker.element,"mouseover",function(){a.graphic.toFront()})})},tooltipFormatter:function(a){return a.point.text}, -animate:function(){}});nb.flag=function(a,b,c,d,e){var f=e&&e.anchorX||a,e=e&&e.anchorY||b;return["M",f,e,"L",a,b+d,a,b,a+c,b,a+c,b+d,a,b+d,"M",f,e,"Z"]};n(["circle","square"],function(a){nb[a+"pin"]=function(b,c,d,e,f){var g=f&&f.anchorX,f=f&&f.anchorY,b=nb[a](b,c,d,e);g&&f&&b.push("M",g,c+e,"L",g,f);return b}});Qa===Ja&&n(["flag","circlepin","squarepin"],function(a){Ja.prototype.symbols[a]=nb[a]});var ob=aa?"touchstart":"mousedown",Zb=aa?"touchmove":"mousemove",$b=aa?"touchend":"mouseup",P=ha("linearGradient", -{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#FFF"],[1,"#CCC"]]),L=[].concat(Yb);L[4]=[fa,[1,2,3,4]];L[5]=[Aa,[1,2,3]];z(T,{navigator:{handles:{backgroundColor:"#FFF",borderColor:"#666"},height:40,margin:10,maskFill:"rgba(255, 255, 255, 0.75)",outlineColor:"#444",outlineWidth:1,series:{type:"areaspline",color:"#4572A7",compare:null,fillOpacity:0.4,dataGrouping:{approximation:"average",groupPixelWidth:2,smoothed:!0,units:L},dataLabels:{enabled:!1},id:"highcharts-navigator-series",lineColor:"#4572A7",lineWidth:1, -marker:{enabled:!1},pointRange:0,shadow:!1},xAxis:{tickWidth:0,lineWidth:0,gridLineWidth:1,tickPixelInterval:200,labels:{align:"left",x:3,y:-4}},yAxis:{gridLineWidth:0,startOnTick:!1,endOnTick:!1,minPadding:0.1,maxPadding:0.1,labels:{enabled:!1},title:{text:null},tickWidth:0}},scrollbar:{height:aa?20:14,barBackgroundColor:P,barBorderRadius:2,barBorderWidth:1,barBorderColor:"#666",buttonArrowColor:"#666",buttonBackgroundColor:P,buttonBorderColor:"#666",buttonBorderRadius:2,buttonBorderWidth:1,rifleColor:"#666", -trackBackgroundColor:ha("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#EEE"],[1,"#FFF"]]),trackBorderColor:"#CCC",trackBorderWidth:1}});Lb.prototype={getAxisTop:function(a){return this.navigatorOptions.top||a-this.height-this.scrollbarHeight-this.chart.options.chart.spacingBottom},drawHandle:function(a,b){var c=this.chart.renderer,d=this.elementsToDestroy,e=this.handles,f=this.navigatorOptions.handles,f={fill:f.backgroundColor,stroke:f.borderColor,"stroke-width":1},g;this.rendered||(e[b]=c.g().css({cursor:"e-resize"}).attr({zIndex:4- -b}).add(),g=c.rect(-4.5,0,9,16,3,1).attr(f).add(e[b]),d.push(g),g=c.path(["M",-1.5,4,"L",-1.5,12,"M",0.5,4,"L",0.5,12]).attr(f).add(e[b]),d.push(g));e[b].translate(this.scrollerLeft+this.scrollbarHeight+parseInt(a,10),this.top+this.height/2-8)},drawScrollbarButton:function(a){var b=this.chart.renderer,c=this.elementsToDestroy,d=this.scrollbarButtons,e=this.scrollbarHeight,f=this.scrollbarOptions,g;this.rendered||(d[a]=b.g().add(this.scrollbarGroup),g=b.rect(-0.5,-0.5,e+1,e+1,f.buttonBorderRadius, -f.buttonBorderWidth).attr({stroke:f.buttonBorderColor,"stroke-width":f.buttonBorderWidth,fill:f.buttonBackgroundColor}).add(d[a]),c.push(g),g=b.path(["M",e/2+(a?-1:1),e/2-3,"L",e/2+(a?-1:1),e/2+3,e/2+(a?2:-2),e/2]).attr({fill:f.buttonArrowColor}).add(d[a]),c.push(g));a&&d[a].attr({translateX:this.scrollerWidth-e})},render:function(a,b,c,d){var e=this.chart,f=e.renderer,g,h,k,i,j=this.scrollbarGroup,l=this.scrollbar,m=this.xAxis,o=this.scrollbarTrack,n=this.scrollbarHeight,A=this.scrollbarEnabled, -q=this.navigatorOptions,s=this.scrollbarOptions,v=this.height,w=this.top,x=this.navigatorEnabled,D=q.outlineWidth,K=D/2,H=this.outlineHeight,y=s.barBorderRadius,z=s.barBorderWidth,B=w+K;if(!isNaN(a)){this.navigatorLeft=g=p(m.left,e.plotLeft+n);this.navigatorWidth=h=p(m.len,e.plotWidth-2*n);this.scrollerLeft=k=g-n;this.scrollerWidth=i=i=h+2*n;if(m.getExtremes){var E=e.xAxis[0].getExtremes(),e=E.dataMin===null,G=m.getExtremes(),I=O(E.dataMin,G.dataMin),E=t(E.dataMax,G.dataMax);!e&&(I!==G.min||E!==G.max)&& -m.setExtremes(I,E,!0,!1)}c=p(c,m.translate(a));d=p(d,m.translate(b));this.zoomedMin=a=F(O(c,d));this.zoomedMax=d=F(t(c,d));this.range=c=d-a;if(!this.rendered){if(x)this.leftShade=f.rect().attr({fill:q.maskFill,zIndex:3}).add(),this.rightShade=f.rect().attr({fill:q.maskFill,zIndex:3}).add(),this.outline=f.path().attr({"stroke-width":D,stroke:q.outlineColor,zIndex:3}).add();if(A)this.scrollbarGroup=j=f.g().add(),l=s.trackBorderWidth,this.scrollbarTrack=o=f.rect().attr({y:-l%2/2,fill:s.trackBackgroundColor, -stroke:s.trackBorderColor,"stroke-width":l,r:s.trackBorderRadius||0,height:n}).add(j),this.scrollbar=l=f.rect().attr({y:-z%2/2,height:n,fill:s.barBackgroundColor,stroke:s.barBorderColor,"stroke-width":z,r:y}).add(j),this.scrollbarRifles=f.path().attr({stroke:s.rifleColor,"stroke-width":1}).add(j)}x&&(this.leftShade.attr({x:g,y:w,width:a,height:v}),this.rightShade.attr({x:g+d,y:w,width:h-d,height:v}),this.outline.attr({d:["M",k,B,"L",g+a+K,B,g+a+K,B+H-n,"M",g+d-K,B+H-n,"L",g+d-K,B,k+i,B]}),this.drawHandle(a+ -K,0),this.drawHandle(d+K,1));A&&(this.drawScrollbarButton(0),this.drawScrollbarButton(1),j.translate(k,u(B+v)),o.attr({width:i}),l.attr({x:u(n+a)+z%2/2,width:c-z}),f=n+a+c/2-0.5,this.scrollbarRifles.attr({d:["M",f-3,n/4,"L",f-3,2*n/3,"M",f,n/4,"L",f,2*n/3,"M",f+3,n/4,"L",f+3,2*n/3],visibility:c>12?"visible":"hidden"}));this.rendered=!0}},addEvents:function(){var a=this.chart;B(a.container,ob,this.mouseDownHandler);B(a.container,Zb,this.mouseMoveHandler);B(document,$b,this.mouseUpHandler)},removeEvents:function(){var a= -this.chart;Q(a.container,ob,this.mouseDownHandler);Q(a.container,Zb,this.mouseMoveHandler);Q(document,$b,this.mouseUpHandler);this.navigatorEnabled&&Q(this.baseSeries,"updatedData",this.updatedDataHandler)},init:function(){var a=this,b=a.chart,c,d,e=a.scrollbarHeight,f=a.navigatorOptions,g=a.height,h=a.top,k,i,j,l=document.body.style,m,o=a.baseSeries,n;a.mouseDownHandler=function(d){var d=b.tracker.normalizeMouseEvent(d),e=a.zoomedMin,f=a.zoomedMax,h=a.top,k=a.scrollbarHeight,j=a.scrollerLeft,o=a.scrollerWidth, -n=a.navigatorLeft,p=a.navigatorWidth,r=a.range,q=d.chartX,s=d.chartY,d=aa?10:7;if(s>h&&sn+e&&qj&&qj+o-k?e+O(10,r):qp&&(f=p-r),f!==e&&b.xAxis[0].setExtremes(c.translate(f,!0),c.translate(f+r,!0),!0,!1))};a.mouseMoveHandler=function(c){var d= -a.scrollbarHeight,e=a.navigatorLeft,f=a.navigatorWidth,g=a.scrollerLeft,h=a.scrollerWidth,k=a.range,c=b.tracker.normalizeMouseEvent(c),c=c.chartX;cg+h-d&&(c=g+h-d);a.grabbedLeft?(j=!0,a.render(0,0,c-e,a.otherHandlePos)):a.grabbedRight?(j=!0,a.render(0,0,a.otherHandlePos,c-e)):a.grabbedCenter&&(j=!0,cf+i-k&&(c=f+i-k),a.render(0,0,c-i,c-i+k))};a.mouseUpHandler=function(){var d=a.zoomedMin,e=a.zoomedMax;j&&b.xAxis[0].setExtremes(c.translate(d,!0),c.translate(e,!0),!0,!1);a.grabbedLeft= -a.grabbedRight=a.grabbedCenter=j=i=null;l.cursor=m};a.updatedDataHandler=function(){var c=o.xAxis,d=c.getExtremes(),e=d.min,f=d.max,g=d.dataMin,d=d.dataMax,h=f-e,i,j,l,m,q;i=k.xData;var p=!!c.setExtremes;j=f>=i[i.length-1];i=e<=g;if(!n)k.options.pointStart=o.xData[0],k.setData(o.options.data,!1),q=!0;i&&(m=g,l=m+h);j&&(l=d,i||(m=t(l-h,k.xData[0])));p&&(i||j)?c.setExtremes(m,l,!0,!1):(q&&b.redraw(!1),a.render(t(e,g),O(f,d)))};var p=b.xAxis.length,q=b.yAxis.length,s=b.setSize;b.extraBottomMargin=a.outlineHeight+ -f.margin;a.top=h=a.getAxisTop(b.chartHeight);if(a.navigatorEnabled){var v=o.options,w=v.data,u=f.series;n=u.data;v.data=u.data=null;a.xAxis=c=new bb(b,y({ordinal:o.xAxis.options.ordinal},f.xAxis,{isX:!0,type:"datetime",index:p,height:g,top:h,offset:0,offsetLeft:e,offsetRight:-e,startOnTick:!1,endOnTick:!1,minPadding:0,maxPadding:0,zoomEnabled:!1}));a.yAxis=d=new bb(b,y(f.yAxis,{alignTicks:!1,height:g,top:h,offset:0,index:q,zoomEnabled:!1}));f=y(o.options,u,{threshold:null,clip:!1,enableMouseTracking:!1, -group:"nav",padXAxis:!1,xAxis:p,yAxis:q,name:"Navigator",showInLegend:!1,isInternal:!0,visible:!0});v.data=w;u.data=n;f.data=n||w;k=b.initSeries(f);B(o,"updatedData",a.updatedDataHandler)}else a.xAxis=c={translate:function(a,c){var d=b.xAxis[0].getExtremes(),f=b.plotWidth-2*e,g=d.dataMin,d=d.dataMax-g;return c?a*d/f+g:f*(a-g)/d}};a.series=k;b.setSize=function(e,f,g){a.top=h=a.getAxisTop(f);if(c&&d)c.options.top=d.options.top=h;s.call(b,e,f,g)};a.addEvents()},destroy:function(){this.removeEvents(); -n([this.xAxis,this.yAxis,this.leftShade,this.rightShade,this.outline,this.scrollbarTrack,this.scrollbarRifles,this.scrollbarGroup,this.scrollbar],function(a){a&&a.destroy&&a.destroy()});this.xAxis=this.yAxis=this.leftShade=this.rightShade=this.outline=this.scrollbarTrack=this.scrollbarRifles=this.scrollbarGroup=this.scrollbar=null;n([this.scrollbarButtons,this.handles,this.elementsToDestroy],function(a){ta(a)})}};Highcharts.Scroller=Lb;z(T,{rangeSelector:{buttonTheme:{width:28,height:16,padding:1, -r:0,zIndex:7}}});T.lang=y(T.lang,{rangeSelectorZoom:"Zoom",rangeSelectorFrom:"From:",rangeSelectorTo:"To:"});Mb.prototype={clickButton:function(a,b,c){var d=this,e=d.chart,f=d.buttons,g=e.xAxis[0],h=g&&g.getExtremes(),k=e.scroller&&e.scroller.xAxis,i=(k=k&&k.getExtremes&&k.getExtremes())&&k.dataMax,j=h&&h.dataMin,l=h&&h.dataMax,k=O(j,p(k&&k.dataMin,j)),i=t(l,p(i,l)),m,n=g&&O(h.max,i),h=new Date(n),l=b.type,j=b.count,r,s,q={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5};if(!(k=== -null||i===null||a===d.selected))q[l]?(r=q[l]*j,m=t(n-r,k)):l==="month"?(h.setMonth(h.getMonth()-j),m=t(h.getTime(),k),r=2592E6*j):l==="ytd"?(h=new Date(0),l=new Date(i),s=l.getFullYear(),h.setFullYear(s),String(s)!==ua("%Y",h)&&h.setFullYear(s-1),m=s=t(k||0,h.getTime()),l=l.getTime(),n=O(i||l,l)):l==="year"?(h.setFullYear(h.getFullYear()-j),m=t(k,h.getTime()),r=31536E6*j):l==="all"&&g&&(m=k,n=i),f[a]&&f[a].setState(2),g?setTimeout(function(){g.setExtremes(m,n,p(c,1),0,{rangeSelectorButton:b});d.selected= -a},1):(e=e.options.xAxis,e[0]=y(e[0],{range:r,min:s}),d.selected=a)},init:function(a){var b=this,c=b.chart,d=c.options.rangeSelector,a=d.buttons||a,e=b.buttons,f=b.leftBox,g=b.rightBox,d=d.selected;c.extraTopMargin=25;b.buttonOptions=a;b.mouseDownHandler=function(){f&&f.blur();g&&g.blur()};B(c.container,ob,b.mouseDownHandler);d!==v&&a[d]&&this.clickButton(d,a[d],!1);B(c,"load",function(){B(c.xAxis[0],"afterSetExtremes",function(){e[b.selected]&&!c.renderer.forExport&&e[b.selected].setState(0);b.selected= -null})})},setInputValue:function(a,b){var c=this.chart.options.rangeSelector,c=a.hasFocus?c.inputEditDateFormat||"%Y-%m-%d":c.inputDateFormat||"%b %e, %Y";if(b)a.HCTime=b;a.value=ua(c,a.HCTime)},drawInput:function(a){var b=this,c=b.chart,d=c.options.rangeSelector,e=b.div,f=a==="min",g;b.boxSpanElements[a]=V("span",{innerHTML:T.lang[f?"rangeSelectorFrom":"rangeSelectorTo"]},d.labelStyle,e);g=V("input",{name:a,className:"highcharts-range-selector",type:"text"},z({width:"80px",height:"16px",border:"1px solid silver", -marginLeft:"5px",marginRight:f?"5px":"0",textAlign:"center"},d.inputStyle),e);g.onfocus=g.onblur=function(a){a=a||window.event||{};g.hasFocus=a.type==="focus";b.setInputValue(g)};g.onchange=function(){var a=g.value,d=Date.parse(a),e=c.xAxis[0].getExtremes();isNaN(d)&&(d=a.split("-"),d=Date.UTC(F(d[0]),F(d[1])-1,F(d[2])));if(!isNaN(d)&&(f&&d>=e.dataMin&&d<=b.rightBox.HCTime||!f&&d<=e.dataMax&&d>=b.leftBox.HCTime))c.xAxis[0].setExtremes(f?d:e.min,f?e.max:d)};return g},render:function(a,b){var c=this, -d=c.chart,e=d.renderer,f=d.container,g=d.options.rangeSelector,h=c.buttons,k=T.lang,i=c.div,i=d.options.chart.style,j=g.buttonTheme,l=g.inputEnabled!==!1,m=j&&j.states,o=d.plotLeft,p;if(!c.rendered&&(c.zoomText=e.text(k.rangeSelectorZoom,o,d.plotTop-10).css(g.labelStyle).add(),p=o+c.zoomText.getBBox().width+5,n(c.buttonOptions,function(a,b){h[b]=e.button(a.text,p,d.plotTop-25,function(){c.clickButton(b,a);c.isActive=!0},j,m&&m.hover,m&&m.select).css({textAlign:"center"}).add();p+=h[b].width+(g.buttonSpacing|| -0);c.selected===b&&h[b].setState(2)}),l))c.divRelative=i=V("div",null,{position:"relative",height:0,fontFamily:i.fontFamily,fontSize:i.fontSize,zIndex:1}),f.parentNode.insertBefore(i,f),c.divAbsolute=c.div=i=V("div",null,z({position:"absolute",top:d.plotTop-25+"px",right:d.chartWidth-d.plotLeft-d.plotWidth+"px"},g.inputBoxStyle),i),c.leftBox=c.drawInput("min"),c.rightBox=c.drawInput("max");l&&(c.setInputValue(c.leftBox,a),c.setInputValue(c.rightBox,b));c.rendered=!0},destroy:function(){var a=this.leftBox, -b=this.rightBox,c=this.boxSpanElements,d=this.divRelative,e=this.divAbsolute,f=this.zoomText;Q(this.chart.container,ob,this.mouseDownHandler);n([this.buttons],function(a){ta(a)});if(f)this.zoomText=f.destroy();if(a)a.onfocus=a.onblur=a.onchange=null;if(b)b.onfocus=b.onblur=b.onchange=null;n([a,b,c.min,c.max,e,d],function(a){Na(a)});this.leftBox=this.rightBox=this.boxSpanElements=this.div=this.divAbsolute=this.divRelative=null}};Highcharts.RangeSelector=Mb;cb.prototype.callbacks.push(function(a){function b(){f= -a.xAxis[0].getExtremes();g.render(t(f.min,f.dataMin),O(f.max,f.dataMax))}function c(){f=a.xAxis[0].getExtremes();h.render(f.min,f.max)}function d(a){g.render(a.min,a.max)}function e(a){h.render(a.min,a.max)}var f,g=a.scroller,h=a.rangeSelector;g&&(B(a.xAxis[0],"afterSetExtremes",d),B(a,"resize",b),b());h&&(B(a.xAxis[0],"afterSetExtremes",e),B(a,"resize",c),c());B(a,"destroy",function(){g&&(Q(a,"resize",b),Q(a.xAxis[0],"afterSetExtremes",d));h&&(Q(a,"resize",c),Q(a.xAxis[0],"afterSetExtremes",e))})}); -Highcharts.StockChart=function(a,b){var c=a.series,d,e={marker:{enabled:!1,states:{hover:{enabled:!0,radius:5}}},shadow:!1,states:{hover:{lineWidth:2}},dataGrouping:{enabled:!0}};a.xAxis=va(la(a.xAxis||{}),function(a){return y({minPadding:0,maxPadding:0,ordinal:!0,title:{text:null},labels:{overflow:"justify"},showLastLabel:!0},a,{type:"datetime",categories:null})});a.yAxis=va(la(a.yAxis||{}),function(a){d=a.opposite;return y({labels:{align:d?"right":"left",x:d?-2:2,y:-2},showLastLabel:!1,title:{text:null}}, -a)});a.series=null;a=y({chart:{panning:!0},navigator:{enabled:!0},scrollbar:{enabled:!0},rangeSelector:{enabled:!0},title:{text:null},tooltip:{shared:!0,crosshairs:!0},legend:{enabled:!1},plotOptions:{line:e,spline:e,area:e,areaspline:e,column:{shadow:!1,borderWidth:0,dataGrouping:{enabled:!0}}}},a,{chart:{inverted:!1}});a.series=c;return new cb(a,b)};var hc=X.init,ic=X.processData,jc=xa.prototype.tooltipFormatter;X.init=function(){hc.apply(this,arguments);var a=this.options.compare;if(a)this.modifyValue= -function(b,c){var d=this.compareValue,b=a==="value"?b-d:b=100*(b/d)-100;if(c)c.change=b;return b}};X.processData=function(){ic.apply(this,arguments);if(this.options.compare)for(var a=0,b=this.processedXData,c=this.processedYData,d=c.length,e=this.xAxis.getExtremes().min;a=e){this.compareValue=c[a];break}};xa.prototype.tooltipFormatter=function(a){a=a.replace("{point.change}",(this.change>0?"+":"")+Ya(this.change,this.series.tooltipOptions.changeDecimals||2)); -return jc.apply(this,[a])};(function(){var a=X.init,b=X.getSegments;X.init=function(){var b,d;a.apply(this,arguments);b=this.chart;(d=this.xAxis)&&d.options.ordinal&&B(this,"updatedData",function(){delete d.ordinalIndex});if(d&&d.options.ordinal&&!d.hasOrdinalExtension){d.hasOrdinalExtension=!0;d.beforeSetTickPositions=function(){var a,b=[],c=!1,e,i=this.getExtremes(),j=i.min,i=i.max,l;if(this.options.ordinal){n(this.series,function(c,d){if(c.visible!==!1&&(b=b.concat(c.processedXData),a=b.length, -d&&a)){b.sort(function(a,b){return a-b});for(d=a-1;d--;)b[d]===b[d+1]&&b.splice(d,1)}});a=b.length;if(a>2){e=b[1]-b[0];for(l=a-1;l--&&!c;)b[l+1]-b[l]!==e&&(c=!0)}c?(this.ordinalPositions=b,c=d.val2lin(j,!0),e=d.val2lin(i,!0),this.ordinalSlope=i=(i-j)/(e-c),this.ordinalOffset=j-c*i):this.ordinalPositions=this.ordinalSlope=this.ordinalOffset=v}};d.val2lin=function(a,b){var c=this.ordinalPositions;if(c){var d=c.length,e,j;for(e=d;e--;)if(c[e]===a){j=e;break}for(e=d-1;e--;)if(a>c[e]||e===0){c=(a-c[e])/ -(c[e+1]-c[e]);j=e+c;break}return b?j:this.ordinalSlope*(j||0)+this.ordinalOffset}else return a};d.lin2val=function(a,b){var c=this.ordinalPositions;if(c){var d=this.ordinalSlope,e=this.ordinalOffset,j=c.length-1,l,m;if(b)a<0?a=c[0]:a>j?a=c[j]:(j=$(a),m=a-j);else for(;j--;)if(l=d*j+e,a>=l){d=d*(j+1)+e;m=(a-l)/(d-l);break}return m!==v&&c[j]!==v?c[j]+(m?m*(c[j+1]-c[j]):0):a}else return a};d.getExtendedPositions=function(){var a=d.series[0].currentDataGrouping,e=d.ordinalIndex,h=a?a.count+a.unitName: -"raw",k=d.getExtremes(),i,j;if(!e)e=d.ordinalIndex={};if(!e[h])i={series:[],getExtremes:function(){return{min:k.dataMin,max:k.dataMax}},options:{ordinal:!0}},n(d.series,function(d){j={xAxis:i,xData:d.xData,chart:b,destroyGroupedData:Rb};j.options={dataGrouping:a?{enabled:!0,forced:!0,approximation:"open",units:[[a.unitName,[a.count]]]}:{enabled:!1}};d.processData.apply(j);i.series.push(j)}),d.beforeSetTickPositions.apply(i),e[h]=i.ordinalPositions;return e[h]};d.getGroupIntervalFactor=function(a, -b,c){for(var d=0,e=c.length,j=[];dc;i[n]j*5||y)p=gb(a,i[m],i[n],e),w=w.concat(p),m=n+1;if(y)break}a=p.info; -if(l&&a.unitRange<=E[sa]){n=w.length-1;for(m=1;mc?a-1:a;for(z=void 0;l--;)m=n[l],c=z-m,z&&c1)o&&n(o,function(a){a.setState()}),k<0?(o=s,s=d.ordinalPositions?d:s):o=d.ordinalPositions?d:s,q=s.ordinalPositions,j>q[q.length-1]&&q.push(j),o=p.apply(o,[u.apply(o,[l,!0])+k,!0]),k=p.apply(s,[u.apply(s, -[m,!0])+k,!0]),o>O(i.dataMin,l)&&ka.xAxis.closestPointRange*e&&d.splice(g+1,0,b.splice(h+1,b.length-h))})}})();z(Highcharts,{Axis:bb,CanVGRenderer:mb,Chart:cb,Color:wa,Legend:xb,Point:xa,Tick:ab,Tooltip:wb,Renderer:Qa,Series:Z, -SVGRenderer:qa,VMLRenderer:Ja,dateFormat:ua,pathAnim:db,getOptions:function(){return T},hasBidiBug:bc,numberFormat:Ya,seriesTypes:U,setOptions:function(a){T=y(T,a);Ib();return T},addEvent:B,removeEvent:Q,createElement:V,discardElement:Na,css:M,each:n,extend:z,map:va,merge:y,pick:p,splat:la,extendClass:ca,pInt:F,product:"Highstock",version:"1.1.6"})})(); +(function(){function y(a,b){var c;a||(a={});for(c in b)a[c]=b[c];return a}function z(){var a,b=arguments.length,c={},d=function(a,b){var c,h;for(h in b)b.hasOwnProperty(h)&&(c=b[h],typeof a!=="object"&&(a={}),a[h]=c&&typeof c==="object"&&Object.prototype.toString.call(c)!=="[object Array]"&&typeof c.nodeType!=="number"?d(a[h]||{},c):b[h]);return a};for(a=0;a3?c.length%3:0;return e+(g?c.substr(0,g)+d:"")+c.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+d)+(f?b+U(a-c).toFixed(f).slice(2):"")}function Ka(a,b){return Array((b||2)+1-String(a).length).join(0)+a}function sa(a,b,c){var d=a[b];a[b]=function(){var a=Array.prototype.slice.call(arguments); +a.unshift(d);return c.apply(this,a)}}function La(a,b){for(var c="{",d=!1,e,f,g,h,i,j=[];(c=a.indexOf(c))!==-1;){e=a.slice(0,c);if(d){f=e.split(":");g=f.shift().split(".");i=g.length;e=b;for(h=0;h-1?h.thousandsSep:"")):e=ya(f,e)}j.push(e);a=a.slice(c+1);c=(d=!d)?"}":"{"}j.push(a);return j.join("")}function yb(a,b,c,d){var e,c=p(c,1);e=a/c;b||(b=[1, +2,2.5,5,10],d&&d.allowDecimals===!1&&(c===1?b=[1,2,5,10]:c<=0.1&&(b=[1/c])));for(d=0;d=I[eb]&&(i.setMilliseconds(0),i.setSeconds(j>=I[Ya]?0:k*W(i.getSeconds()/k)));if(j>=I[Ya])i[Nb](j>=I[za]?0:k*W(i[Ab]()/k));if(j>=I[za])i[Ob](j>=I[ga]?0:k*W(i[Bb]()/k));if(j>=I[ga])i[Cb](j>=I[Na]?1:k*W(i[Oa]()/k));j>=I[Na]&&(i[Pb](j>=I[ta]?0:k*W(i[lb]()/k)),h=i[mb]());j>=I[ta]&&(h-=h%k,i[Qb](h));if(j===I[Ma])i[Cb](i[Oa]()- +i[Db]()+p(d,1));b=1;h=i[mb]();for(var d=i.getTime(),m=i[lb](),l=i[Oa](),o=g?0:(864E5+i.getTimezoneOffset()*6E4)%864E5;dc&&(c=a[b]);return c}function Aa(a,b){for(var c in a)a[c]&&a[c]!==b&&a[c].destroy&&a[c].destroy(),delete a[c]}function Za(a){ob||(ob=aa(Qa));a&&ob.appendChild(a);ob.innerHTML=""}function Ba(a,b){var c="Highcharts error #"+a+": www.highcharts.com/errors/"+a;if(b)throw c;else Y.console&&console.log(c)}function oa(a){return parseFloat(a.toPrecision(14))} +function $a(a,b){Ra=p(a,b.animation)}function Tb(){var a=M.global.useUTC,b=a?"getUTC":"get",c=a?"setUTC":"set";nb=a?Date.UTC:function(a,b,c,g,h,i){return(new Date(a,b,p(c,1),p(g,0),p(h,0),p(i,0))).getTime()};Ab=b+"Minutes";Bb=b+"Hours";Db=b+"Day";Oa=b+"Date";lb=b+"Month";mb=b+"FullYear";Nb=c+"Minutes";Ob=c+"Hours";Cb=c+"Date";Pb=c+"Month";Qb=c+"FullYear"}function Ca(){}function ab(a,b,c,d){this.axis=a;this.pos=b;this.type=c||"";this.isNew=!0;!c&&!d&&this.addLabel()}function Fb(a,b){this.axis=a;if(b)this.options= +b,this.id=b.id}function Ub(a,b,c,d,e,f){var g=a.chart.inverted;this.axis=a;this.isNegative=c;this.options=b;this.x=d;this.stack=e;this.percent=f==="percent";this.alignOptions={align:b.align||(g?c?"left":"right":"center"),verticalAlign:b.verticalAlign||(g?"middle":c?"bottom":"top"),y:p(b.y,g?4:c?14:-6),x:p(b.x,g?c?-6:6:0)};this.textAlign=b.textAlign||(g?c?"right":"left":"center")}function Da(){this.init.apply(this,arguments)}function Gb(){this.init.apply(this,arguments)}function pb(a,b){this.init(a, +b)}function Hb(a,b){this.init(a,b)}function Sa(){this.init.apply(this,arguments)}function Ib(a){var b=a.options,c=b.navigator,d=c.enabled,b=b.scrollbar,e=b.enabled,f=d?c.height:0,g=e?b.height:0,h=c.baseSeries;this.baseSeries=a.series[h]||typeof h==="string"&&a.get(h)||a.series[0];this.handles=[];this.scrollbarButtons=[];this.elementsToDestroy=[];this.chart=a;this.height=f;this.scrollbarHeight=g;this.scrollbarEnabled=e;this.navigatorEnabled=d;this.navigatorOptions=c;this.scrollbarOptions=b;this.outlineHeight= +f+g;this.init()}function Jb(a){this.init(a)}var w,H=document,Y=window,P=Math,r=P.round,W=P.floor,pa=P.ceil,t=P.max,A=P.min,U=P.abs,ha=P.cos,ka=P.sin,bb=P.PI,qb=bb*2/360,Ta=navigator.userAgent,Vb=Y.opera,Xa=/msie/i.test(Ta)&&!Vb,rb=H.documentMode===8,sb=/AppleWebKit/.test(Ta),tb=/Firefox/.test(Ta),ub=/(Mobile|Android|Windows Phone)/.test(Ta),Ea="http://www.w3.org/2000/svg",ca=!!H.createElementNS&&!!H.createElementNS(Ea,"svg").createSVGRect,cc=tb&&parseInt(Ta.split("Firefox/")[1],10)<4,ia=!ca&&!Xa&& +!!H.createElement("canvas").getContext,cb,gb=H.documentElement.ontouchstart!==w,Wb={},Kb=0,ob,M,ya,Ra,Lb,I,qa=function(){},Ua=[],Qa="div",ba="none",Xb="rgba(192,192,192,"+(ca?1.0E-4:0.002)+")",kb="millisecond",eb="second",Ya="minute",za="hour",ga="day",Ma="week",Na="month",ta="year",Yb="stroke-width",nb,Ab,Bb,Db,Oa,lb,mb,Nb,Ob,Cb,Pb,Qb,Q={};Y.Highcharts=Y.Highcharts?Ba(16,!0):{};ya=function(a,b,c){if(!v(b)||isNaN(b))return"Invalid date";var a=p(a,"%Y-%m-%d %H:%M:%S"),d=new Date(b),e,f=d[Bb](),g=d[Db](), +h=d[Oa](),i=d[lb](),j=d[mb](),k=M.lang,m=k.weekdays,d=y({a:m[g].substr(0,3),A:m[g],d:Ka(h),e:h,b:k.shortMonths[i],B:k.months[i],m:Ka(i+1),y:j.toString().substr(2,2),Y:j,H:Ka(f),I:Ka(f%12||12),l:f%12||12,M:Ka(d[Ab]()),p:f<12?"AM":"PM",P:f<12?"am":"pm",S:Ka(d.getSeconds()),L:Ka(r(b%1E3),3)},Highcharts.dateFormats);for(e in d)for(;a.indexOf("%"+e)!==-1;)a=a.replace("%"+e,typeof d[e]==="function"?d[e](b):d[e]);return c?a.substr(0,1).toUpperCase()+a.substr(1):a};Rb.prototype={wrapColor:function(a){if(this.color>= +a)this.color=0},wrapSymbol:function(a){if(this.symbol>=a)this.symbol=0}};I=jb(kb,1,eb,1E3,Ya,6E4,za,36E5,ga,864E5,Ma,6048E5,Na,26784E5,ta,31556952E3);Lb={init:function(a,b,c){var b=b||"",d=a.shift,e=b.indexOf("C")>-1,f=e?7:3,g,b=b.split(" "),c=[].concat(c),h,i,j=function(a){for(g=a.length;g--;)a[g]==="M"&&a.splice(g+1,0,a[g+1],a[g+2],a[g+1],a[g+2])};e&&(j(b),j(c));a.isArea&&(h=b.splice(b.length-6,6),i=c.splice(c.length-6,6));if(d<=c.length/f)for(;d--;)c=[].concat(c).splice(0,f).concat(c);a.shift= +0;if(b.length)for(a=c.length;b.length{point.key}

    ',pointFormat:'{series.name}: {point.y}
    ',shadow:!0,snap:ub?25:10,style:{color:"#333333",cursor:"default",fontSize:"12px",padding:"8px",whiteSpace:"nowrap"}},credits:{enabled:!0,text:"Highcharts.com",href:"http://www.highcharts.com",position:{align:"right",x:-10,verticalAlign:"bottom",y:-5},style:{cursor:"pointer", +color:"#909090",fontSize:"9px"}}};var S=M.plotOptions,R=S.line;Tb();var wa=function(a){var b=[],c,d;(function(a){a&&a.stops?d=Fa(a.stops,function(a){return wa(a[1])}):(c=/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(a))?b=[C(c[1]),C(c[2]),C(c[3]),parseFloat(c[4],10)]:(c=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(a))?b=[C(c[1],16),C(c[2],16),C(c[3],16),1]:(c=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(a))&& +(b=[C(c[1]),C(c[2]),C(c[3]),1])})(a);return{get:function(c){var f;d?(f=z(a),f.stops=[].concat(f.stops),n(d,function(a,b){f.stops[b]=[f.stops[b][0],a.get(c)]})):f=b&&!isNaN(b[0])?c==="rgb"?"rgb("+b[0]+","+b[1]+","+b[2]+")":c==="a"?b[3]:"rgba("+b.join(",")+")":a;return f},brighten:function(a){if(d)n(d,function(b){b.brighten(a)});else if(Ja(a)&&a!==0){var c;for(c=0;c<3;c++)b[c]+=C(a*255),b[c]<0&&(b[c]=0),b[c]>255&&(b[c]=255)}return this},rgba:b,setOpacity:function(a){b[3]=a;return this}}};Ca.prototype= +{init:function(a,b){this.element=b==="span"?aa(b):H.createElementNS(Ea,b);this.renderer=a;this.attrSetters={}},opacity:1,animate:function(a,b,c){b=p(b,Ra,!0);hb(this);if(b){b=z(b);if(c)b.complete=c;Mb(this,a,b)}else this.attr(a),c&&c()},attr:function(a,b){var c,d,e,f,g=this.element,h=g.nodeName.toLowerCase(),i=this.renderer,j,k=this.attrSetters,m=this.shadows,l,o,q=this;ma(a)&&v(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,h==="circle"?c={x:"cx",y:"cy"}[c]||c:c==="strokeWidth"&&(c="stroke-width"),q=G(g,c)|| +this[c]||0,c!=="d"&&c!=="visibility"&&(q=parseFloat(q));else{for(c in a)if(j=!1,d=a[c],e=k[c]&&k[c].call(this,d,c),e!==!1){e!==w&&(d=e);if(c==="d")d&&d.join&&(d=d.join(" ")),/(NaN| {2}|^$)/.test(d)&&(d="M 0 0");else if(c==="x"&&h==="text")for(e=0;el&&/[ \-]/.test(b.textContent||b.innerText))L(b,{width:l+"px",display:"block",whiteSpace:"normal"}),k=l;l=a.fontMetrics(b.style.fontSize).b;u=q<0&&-k;s=o<0&&-m;O=q*o<0;u+=o*l*(O?1-h:h);s-=q*l*(j? +O?h:1-h:1);i&&(u-=k*h*(q<0?-1:1),j&&(s-=m*h*(o<0?-1:1)),L(b,{textAlign:g}));this.xCorr=u;this.yCorr=s}L(b,{left:e+u+"px",top:f+s+"px"});if(sb)m=b.offsetHeight;this.cTT=x}}else this.alignOnAdd=!0},updateTransform:function(){var a=this.translateX||0,b=this.translateY||0,c=this.scaleX,d=this.scaleY,e=this.inverted,f=this.rotation;e&&(a+=this.attr("width"),b+=this.attr("height"));a=["translate("+a+","+b+")"];e?a.push("rotate(90) scale(-1,1)"):f&&a.push("rotate("+f+" "+(this.x||0)+" "+(this.y||0)+")"); +(v(c)||v(d))&&a.push("scale("+p(c,1)+" "+p(d,1)+")");a.length&&G(this.element,"transform",a.join(" "))},toFront:function(){var a=this.element;a.parentNode.appendChild(a);return this},align:function(a,b,c){var d,e,f,g,h={};e=this.renderer;f=e.alignedObjects;if(a){if(this.alignOptions=a,this.alignByTranslate=b,!c||ma(c))this.alignTo=d=c||"renderer",na(f,this),f.push(this),c=null}else a=this.alignOptions,b=this.alignByTranslate,d=this.alignTo;c=p(c,e[d],e);d=a.align;e=a.verticalAlign;f=(c.x||0)+(a.x|| +0);g=(c.y||0)+(a.y||0);if(d==="right"||d==="center")f+=(c.width-(a.width||0))/{right:1,center:2}[d];h[b?"translateX":"x"]=r(f);if(e==="bottom"||e==="middle")g+=(c.height-(a.height||0))/({bottom:1,middle:2}[e]||1);h[b?"translateY":"y"]=r(g);this[this.placed?"animate":"attr"](h);this.placed=!0;this.alignAttr=h;return this},getBBox:function(){var a=this.bBox,b=this.renderer,c,d=this.rotation;c=this.element;var e=this.styles,f=d*qb;if(!a){if(c.namespaceURI===Ea||b.forExport){try{a=c.getBBox?y({},c.getBBox()): +{width:c.offsetWidth,height:c.offsetHeight}}catch(g){}if(!a||a.width<0)a={width:0,height:0}}else a=this.htmlGetBBox();if(b.isSVG){b=a.width;c=a.height;if(Xa&&e&&e.fontSize==="11px"&&c.toPrecision(3)==="22.7")a.height=c=14;if(d)a.width=U(c*ka(f))+U(b*ha(f)),a.height=U(c*ha(f))+U(b*ka(f))}this.bBox=a}return a},show:function(){return this.attr({visibility:"visible"})},hide:function(){return this.attr({visibility:"hidden"})},fadeOut:function(a){var b=this;b.animate({opacity:0},{duration:a||150,complete:function(){b.hide()}})}, +add:function(a){var b=this.renderer,c=a||b,d=c.element||b.box,e=d.childNodes,f=this.element,g=G(f,"zIndex"),h;if(a)this.parentGroup=a;this.parentInverted=a&&a.inverted;this.textStr!==void 0&&b.buildText(this);if(g)c.handleZ=!0,g=C(g);if(c.handleZ)for(c=0;cg||!v(g)&&v(b))){d.insertBefore(f,a);h=!0;break}h||d.appendChild(f);this.added=!0;K(this,"add");return this},safeRemoveChild:function(a){var b=a.parentNode;b&&b.removeChild(a)},destroy:function(){var a= +this,b=a.element||{},c=a.shadows,d,e;b.onclick=b.onmouseout=b.onmouseover=b.onmousemove=b.point=null;hb(a);if(a.clipPath)a.clipPath=a.clipPath.destroy();if(a.stops){for(e=0;e/g,'').replace(/<(i|em)>/g,'').replace(/
    /g,"").split(//g),f=b.childNodes,g=/style="([^"]+)"/,h=/href="([^"]+)"/,i=G(b,"x"),j=a.styles,k=j&&j.width&&C(j.width),m=j&&j.lineHeight,l=f.length;l--;)b.removeChild(f[l]);k&&!a.added&&this.box.appendChild(b); +e[e.length-1]===""&&e.pop();n(e,function(e,f){var l,u=0,e=e.replace(//g,"|||");l=e.split("|||");n(l,function(e){if(e!==""||l.length===1){var o={},n=H.createElementNS(Ea,"tspan"),p;g.test(e)&&(p=e.match(g)[1].replace(/(;| |^)color([ :])/,"$1fill$2"),G(n,"style",p));h.test(e)&&!d&&(G(n,"onclick",'location.href="'+e.match(h)[1]+'"'),L(n,{cursor:"pointer"}));e=(e.replace(/<(.|\n)*?>/g,"")||" ").replace(/</g,"<").replace(/>/g,">");n.appendChild(H.createTextNode(e)); +u?o.dx=0:o.x=i;G(n,o);!u&&f&&(!ca&&d&&L(n,{display:"block"}),G(n,"dy",m||c.fontMetrics(/px$/.test(n.style.fontSize)?n.style.fontSize:j.fontSize).h,sb&&n.offsetHeight));b.appendChild(n);u++;if(k)for(var e=e.replace(/([^\^])-/g,"$1- ").split(" "),E,B=[];e.length||B.length;)delete a.bBox,E=a.getBBox().width,o=E>k,!o||e.length===1?(e=B,B=[],e.length&&(n=H.createElementNS(Ea,"tspan"),G(n,{dy:m||16,x:i}),p&&G(n,"style",p),b.appendChild(n),E>k&&(k=E))):(n.removeChild(n.firstChild),B.unshift(e.pop())),e.length&& +n.appendChild(H.createTextNode(e.join(" ").replace(/- /g,"-")))}})})},button:function(a,b,c,d,e,f,g){var h=this.label(a,b,c,null,null,null,null,null,"button"),i=0,j,k,m,l,o,a={x1:0,y1:0,x2:0,y2:1},e=z({"stroke-width":1,stroke:"#CCCCCC",fill:{linearGradient:a,stops:[[0,"#FEFEFE"],[1,"#F6F6F6"]]},r:2,padding:5,style:{color:"black"}},e);m=e.style;delete e.style;f=z(e,{stroke:"#68A",fill:{linearGradient:a,stops:[[0,"#FFF"],[1,"#ACF"]]}},f);l=f.style;delete f.style;g=z(e,{stroke:"#68A",fill:{linearGradient:a, +stops:[[0,"#9BD"],[1,"#CDF"]]}},g);o=g.style;delete g.style;F(h.element,"mouseenter",function(){h.attr(f).css(l)});F(h.element,"mouseleave",function(){j=[e,f,g][i];k=[m,l,o][i];h.attr(j).css(k)});h.setState=function(a){(i=a)?a===2&&h.attr(g).css(o):h.attr(e).css(m)};return h.on("click",function(){d.call(h)}).attr(e).css(y({cursor:"default"},m))},crispLine:function(a,b){a[1]===a[4]&&(a[1]=a[4]=r(a[1])-b%2/2);a[2]===a[5]&&(a[2]=a[5]=r(a[2])+b%2/2);return a},path:function(a){var b={fill:ba};Wa(a)?b.d= +a:da(a)&&y(b,a);return this.createElement("path").attr(b)},circle:function(a,b,c){a=da(a)?a:{x:a,y:b,r:c};return this.createElement("circle").attr(a)},arc:function(a,b,c,d,e,f){if(da(a))b=a.y,c=a.r,d=a.innerR,e=a.start,f=a.end,a=a.x;return this.symbol("arc",a||0,b||0,c||0,c||0,{innerR:d||0,start:e||0,end:f||0})},rect:function(a,b,c,d,e,f){e=da(a)?a.r:e;e=this.createElement("rect").attr({rx:e,ry:e,fill:ba});return e.attr(da(a)?a:e.crisp(f,a,b,t(c,0),t(d,0)))},setSize:function(a,b,c){var d=this.alignedObjects, +e=d.length;this.width=a;this.height=b;for(this.boxWrapper[p(c,!0)?"animate":"attr"]({width:a,height:b});e--;)d[e].align()},g:function(a){var b=this.createElement("g");return v(a)?b.attr({"class":"highcharts-"+a}):b},image:function(a,b,c,d,e){var f={preserveAspectRatio:ba};arguments.length>1&&y(f,{x:b,y:c,width:d,height:e});f=this.createElement("image").attr(f);f.element.setAttributeNS?f.element.setAttributeNS("http://www.w3.org/1999/xlink","href",a):f.element.setAttribute("hc-svg-href",a);return f}, +symbol:function(a,b,c,d,e,f){var g,h=this.symbols[a],h=h&&h(r(b),r(c),d,e,f),i=/^url\((.*?)\)$/,j,k;if(h)g=this.path(h),y(g,{symbolName:a,x:b,y:c,width:d,height:e}),f&&y(g,f);else if(i.test(a))k=function(a,b){a.element&&(a.attr({width:b[0],height:b[1]}),a.alignByTranslate||a.translate(r((d-b[0])/2),r((e-b[1])/2)))},j=a.match(i)[1],a=Wb[j],g=this.image(j).attr({x:b,y:c}),g.isImg=!0,a?k(g,a):(g.attr({width:0,height:0}),aa("img",{onload:function(){k(g,Wb[j]=[this.width,this.height])},src:j}));return g}, +symbols:{circle:function(a,b,c,d){var e=0.166*c;return["M",a+c/2,b,"C",a+c+e,b,a+c+e,b+d,a+c/2,b+d,"C",a-e,b+d,a-e,b,a+c/2,b,"Z"]},square:function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c,b+d,a,b+d,"Z"]},triangle:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d,a,b+d,"Z"]},"triangle-down":function(a,b,c,d){return["M",a,b,"L",a+c,b,a+c/2,b+d,"Z"]},diamond:function(a,b,c,d){return["M",a+c/2,b,"L",a+c,b+d/2,a+c/2,b+d,a,b+d/2,"Z"]},arc:function(a,b,c,d,e){var f=e.start,c=e.r||c||d,g=e.end-0.001,d=e.innerR, +h=e.open,i=ha(f),j=ka(f),k=ha(g),g=ka(g),e=e.end-f');if(b)c=e||b==="span"||b==="img"?c.join(""):a.prepVML(c),this.element=aa(c);this.renderer=a;this.attrSetters={}},add:function(a){var b=this.renderer, +c=this.element,d=b.box,d=a?a.element||a:d;a&&a.inverted&&b.invertChild(c,d);d.appendChild(c);this.added=!0;this.alignOnAdd&&!this.deferUpdateTransform&&this.updateTransform();K(this,"add");return this},updateTransform:Ca.prototype.htmlUpdateTransform,attr:function(a,b){var c,d,e,f=this.element||{},g=f.style,h=f.nodeName,i=this.renderer,j=this.symbolName,k,m=this.shadows,l,o=this.attrSetters,q=this;ma(a)&&v(b)&&(c=a,a={},a[c]=b);if(ma(a))c=a,q=c==="strokeWidth"||c==="stroke-width"?this.strokeweight: +this[c];else for(c in a)if(d=a[c],l=!1,e=o[c]&&o[c].call(this,d,c),e!==!1&&d!==null){e!==w&&(d=e);if(j&&/^(x|y|r|start|end|width|height|innerR|anchorX|anchorY)/.test(c))k||(this.symbolAttr(a),k=!0),l=!0;else if(c==="d"){d=d||[];this.d=d.join(" ");e=d.length;l=[];for(var n;e--;)if(Ja(d[e]))l[e]=r(d[e]*10)-5;else if(d[e]==="Z")l[e]="x";else if(l[e]=d[e],d.isArc&&(d[e]==="wa"||d[e]==="at"))n=d[e]==="wa"?1:-1,l[e+5]===l[e+7]&&(l[e+7]-=n),l[e+6]===l[e+8]&&(l[e+8]-=n);d=l.join(" ")||"x";f.path=d;if(m)for(e= +m.length;e--;)m[e].path=m[e].cutOff?this.cutOffPath(d,m[e].cutOff):d;l=!0}else if(c==="visibility"){if(m)for(e=m.length;e--;)m[e].style[c]=d;h==="DIV"&&(d=d==="hidden"?"-999em":0,rb||(g[c]=d?"visible":"hidden"),c="top");g[c]=d;l=!0}else if(c==="zIndex")d&&(g[c]=d),l=!0;else if(va(c,["x","y","width","height"])!==-1)this[c]=d,c==="x"||c==="y"?c={x:"left",y:"top"}[c]:d=t(0,d),this.updateClipping?(this[c]=d,this.updateClipping()):g[c]=d,l=!0;else if(c==="class"&&h==="DIV")f.className=d;else if(c==="stroke")d= +i.color(d,f,c),c="strokecolor";else if(c==="stroke-width"||c==="strokeWidth")f.stroked=d?!0:!1,c="strokeweight",this[c]=d,Ja(d)&&(d+="px");else if(c==="dashstyle")(f.getElementsByTagName("stroke")[0]||aa(i.prepVML([""]),null,null,f))[c]=d||"solid",this.dashstyle=d,l=!0;else if(c==="fill")if(h==="SPAN")g.color=d;else{if(h!=="IMG")f.filled=d!==ba?!0:!1,d=i.color(d,f,c,this),c="fillcolor"}else if(c==="opacity")l=!0;else if(h==="shape"&&c==="rotation")this[c]=d,f.style.left=-r(ka(d*qb)+1)+"px", +f.style.top=r(ha(d*qb))+"px";else if(c==="translateX"||c==="translateY"||c==="rotation")this[c]=d,this.updateTransform(),l=!0;else if(c==="text")this.bBox=null,f.innerHTML=d,l=!0;l||(rb?f[c]=d:G(f,c,d))}return q},clip:function(a){var b=this,c;a?(c=a.members,na(c,b),c.push(b),b.destroyClip=function(){na(c,b)},a=a.getCSS(b)):(b.destroyClip&&b.destroyClip(),a={clip:rb?"inherit":"rect(auto)"});return b.css(a)},css:Ca.prototype.htmlCss,safeRemoveChild:function(a){a.parentNode&&Za(a)},destroy:function(){this.destroyClip&& +this.destroyClip();return Ca.prototype.destroy.apply(this)},on:function(a,b){this.element["on"+a]=function(){var a=Y.event;a.target=a.srcElement;b(a)};return this},cutOffPath:function(a,b){var c,a=a.split(/[ ,]/);c=a.length;if(c===9||c===11)a[c-4]=a[c-2]=C(a[c-2])-10*b;return a.join(" ")},shadow:function(a,b,c){var d=[],e,f=this.element,g=this.renderer,h,i=f.style,j,k=f.path,m,l,o,q;k&&typeof k.value!=="string"&&(k="x");l=k;if(a){o=p(a.width,3);q=(a.opacity||0.15)/o;for(e=1;e<=3;e++){m=o*2+1-2*e; +c&&(l=this.cutOffPath(k.value,m+0.5));j=[''];h=aa(g.prepVML(j),null,{left:C(i.left)+p(a.offsetX,1),top:C(i.top)+p(a.offsetY,1)});if(c)h.cutOff=m+1;j=[''];aa(g.prepVML(j),null,null,h);b?b.element.appendChild(h):f.parentNode.insertBefore(h,f);d.push(h)}this.shadows=d}return this}},Z=ea(Ca,Z),Z={Element:Z,isIE8:Ta.indexOf("MSIE 8.0")> +-1,init:function(a,b,c){var d,e;this.alignedObjects=[];d=this.createElement(Qa);e=d.element;e.style.position="relative";a.appendChild(d.element);this.isVML=!0;this.box=e;this.boxWrapper=d;this.setSize(b,c,!1);if(!H.namespaces.hcv)H.namespaces.add("hcv","urn:schemas-microsoft-com:vml"),H.createStyleSheet().cssText="hcv\\:fill, hcv\\:path, hcv\\:shape, hcv\\:stroke{ behavior:url(#default#VML); display: inline-block; } "},isHidden:function(){return!this.box.offsetWidth},clipRect:function(a,b,c,d){var e= +this.createElement(),f=da(a);return y(e,{members:[],left:f?a.x:a,top:f?a.y:b,width:f?a.width:c,height:f?a.height:d,getCSS:function(a){var b=a.element,c=b.nodeName,a=a.inverted,d=this.top-(c==="shape"?b.offsetTop:0),e=this.left,b=e+this.width,f=d+this.height,d={clip:"rect("+r(a?e:d)+"px,"+r(a?f:b)+"px,"+r(a?b:f)+"px,"+r(a?d:e)+"px)"};!a&&rb&&c==="DIV"&&y(d,{width:b+"px",height:f+"px"});return d},updateClipping:function(){n(e.members,function(a){a.css(e.getCSS(a))})}})},color:function(a,b,c,d){var e= +this,f,g=/^rgba/,h,i,j=ba;a&&a.linearGradient?i="gradient":a&&a.radialGradient&&(i="pattern");if(i){var k,m,l=a.linearGradient||a.radialGradient,o,q,p,u,s,x="",a=a.stops,N,fa=[],E=function(){h=[''];aa(e.prepVML(h),null,null,b)};o=a[0];N=a[a.length-1];o[0]>0&&a.unshift([0,o[1]]);N[0]<1&&a.push([1,N[1]]);n(a,function(a,b){g.test(a[1])?(f=wa(a[1]),k=f.get("rgb"),m=f.get("a")):(k=a[1],m=1); +fa.push(a[0]*100+"% "+k);b?(p=m,u=k):(q=m,s=k)});if(c==="fill")if(i==="gradient")c=l.x1||l[0]||0,a=l.y1||l[1]||0,o=l.x2||l[2]||0,l=l.y2||l[3]||0,x='angle="'+(90-P.atan((l-a)/(o-c))*180/bb)+'"',E();else{var j=l.r,B=j*2,T=j*2,t=l.cx,w=l.cy,v=b.radialReference,r,j=function(){v&&(r=d.getBBox(),t+=(v[0]-r.x)/r.width-0.5,w+=(v[1]-r.y)/r.height-0.5,B*=v[2]/r.width,T*=v[2]/r.height);x='src="'+M.global.VMLRadialGradientURL+'" size="'+B+","+T+'" origin="0.5,0.5" position="'+t+","+w+'" color2="'+s+'" ';E()}; +d.added?j():F(d,"add",j);j=u}else j=k}else if(g.test(a)&&b.tagName!=="IMG")f=wa(a),h=["<",c,' opacity="',f.get("a"),'"/>'],aa(this.prepVML(h),null,null,b),j=f.get("rgb");else{j=b.getElementsByTagName(c);if(j.length)j[0].opacity=1,j[0].type="solid";j=a}return j},prepVML:function(a){var b=this.isIE8,a=a.join("");b?(a=a.replace("/>",' xmlns="urn:schemas-microsoft-com:vml" />'),a=a.indexOf('style="')===-1?a.replace("/>",' style="display:inline-block;behavior:url(#default#VML);" />'):a.replace('style="', +'style="display:inline-block;behavior:url(#default#VML);')):a=a.replace("<","1&&f.attr({x:b,y:c,width:d,height:e});return f},rect:function(a,b,c,d,e,f){if(da(a))b=a.y,c=a.width,d=a.height,f=a.strokeWidth,a=a.x;var g=this.symbol("rect");g.r=e;return g.attr(g.crisp(f,a,b,t(c,0),t(d,0)))},invertChild:function(a,b){var c=b.style;L(a,{flip:"x",left:C(c.width)-1,top:C(c.height)-1,rotation:-90})},symbols:{arc:function(a,b,c,d,e){var f=e.start,g=e.end,h=e.r||c||d,c=e.innerR,d=ha(f),i=ka(f),j=ha(g),k=ka(g);if(g- +f===0)return["x"];f=["wa",a-h,b-h,a+h,b+h,a+h*d,b+h*i,a+h*j,b+h*k];e.open&&!c&&f.push("e","M",a,b);f.push("at",a-c,b-c,a+c,b+c,a+c*j,b+c*k,a+c*d,b+c*i,"x","e");f.isArc=!0;return f},circle:function(a,b,c,d,e){e&&e.isCircle&&(a-=c/2,b-=d/2);return["wa",a,b,a+c,b+d,a+c,b+d/2,a+c,b+d/2,"e"]},rect:function(a,b,c,d,e){var f=a+c,g=b+d,h;!v(e)||!e.r?f=Ga.prototype.symbols.square.apply(0,arguments):(h=A(e.r,c,d),f=["M",a+h,b,"L",f-h,b,"wa",f-2*h,b,f,b+2*h,f-h,b,f,b+h,"L",f,g-h,"wa",f-2*h,g-2*h,f,g,f,g-h,f- +h,g,"L",a+h,g,"wa",a,g-2*h,a+2*h,g,a+h,g,a,g-h,"L",a,b+h,"wa",a,b,a+2*h,b+2*h,a,b+h,a+h,b,"x","e"]);return f}}},Highcharts.VMLRenderer=ib=function(){this.init.apply(this,arguments)},ib.prototype=z(Ga.prototype,Z),cb=ib;var $b;if(ia)Highcharts.CanVGRenderer=Z=function(){Ea="http://www.w3.org/1999/xhtml"},Z.prototype.symbols={},$b=function(){function a(){var a=b.length,d;for(d=0;dj&&(c=!1)):h+k>l&&(h=l-k,d&&h+m0&&b.height>0){f=z({align:c&&k&&"center",x:c?!k&&4:10,verticalAlign:!c&&k&&"middle",y:c?k?16:10:k?6:-4,rotation:c&&!k&&90},f);if(!g)a.label=g=N.text(f.text,0,0).attr({align:f.textAlign||f.align,rotation:f.rotation,zIndex:s}).css(f.style).add();b=[q[1],q[4],p(q[6],q[1])];q=[q[2],q[5],p(q[7],q[2])];c=Pa(b);k=Pa(q);g.align(f,!1,{x:c,y:k,width:ua(b)-c,height:ua(q)-k});g.show()}else g&&g.hide(); +return a},destroy:function(){na(this.axis.plotLinesAndBands,this);Aa(this,this.axis)}};Ub.prototype={destroy:function(){Aa(this,this.axis)},setTotal:function(a){this.cum=this.total=a},render:function(a){var b=this.options,c=b.format,c=c?La(c,this):b.formatter.call(this);this.label?this.label.attr({text:c,visibility:"hidden"}):this.label=this.axis.chart.renderer.text(c,0,0,b.useHTML).css(b.style).attr({align:this.textAlign,rotation:b.rotation,visibility:"hidden"}).add(a)},setOffset:function(a,b){var c= +this.axis,d=c.chart,e=d.inverted,f=this.isNegative,g=c.translate(this.percent?100:this.total,0,0,0,1),c=c.translate(0),c=U(g-c),h=d.xAxis[0].translate(this.x)+a,i=d.plotHeight,f={x:e?f?g:g-c:h,y:e?i-h-b:f?i-g-c:i-g,width:e?c:b,height:e?b:c};if(e=this.label)e.align(this.alignOptions,null,f),f=e.alignAttr,e.attr({visibility:this.options.crop===!1||d.isInsidePlot(f.x,f.y)?ca?"inherit":"visible":"hidden"})}};Da.prototype={defaultOptions:{dateTimeLabelFormats:{millisecond:"%H:%M:%S.%L",second:"%H:%M:%S", +minute:"%H:%M",hour:"%H:%M",day:"%e. %b",week:"%e. %b",month:"%b '%y",year:"%Y"},endOnTick:!1,gridLineColor:"#C0C0C0",labels:D,lineColor:"#C0D0E0",lineWidth:1,minPadding:0.01,maxPadding:0.01,minorGridLineColor:"#E0E0E0",minorGridLineWidth:1,minorTickColor:"#A0A0A0",minorTickLength:2,minorTickPosition:"outside",startOfWeek:1,startOnTick:!1,tickColor:"#C0D0E0",tickLength:5,tickmarkPlacement:"between",tickPixelInterval:100,tickPosition:"outside",tickWidth:1,title:{align:"middle",style:{color:"#4d759e", +fontWeight:"bold"}},type:"linear"},defaultYAxisOptions:{endOnTick:!0,gridLineWidth:1,tickPixelInterval:72,showLastLabel:!0,labels:{align:"right",x:-8,y:3},lineWidth:0,maxPadding:0.05,minPadding:0.05,startOnTick:!0,tickWidth:0,title:{rotation:270,text:"Values"},stackLabels:{enabled:!1,formatter:function(){return xa(this.total,-1)},style:D.style}},defaultLeftAxisOptions:{labels:{align:"right",x:-8,y:null},title:{rotation:270}},defaultRightAxisOptions:{labels:{align:"left",x:8,y:null},title:{rotation:90}}, +defaultBottomAxisOptions:{labels:{align:"center",x:0,y:14},title:{rotation:0}},defaultTopAxisOptions:{labels:{align:"center",x:0,y:-5},title:{rotation:0}},init:function(a,b){var c=b.isX;this.horiz=a.inverted?!c:c;this.xOrY=(this.isXAxis=c)?"x":"y";this.opposite=b.opposite;this.side=this.horiz?this.opposite?0:2:this.opposite?1:3;this.setOptions(b);var d=this.options,e=d.type;this.labelFormatter=d.labels.formatter||this.defaultLabelFormatter;this.staggerLines=this.horiz&&d.labels.staggerLines;this.userOptions= +b;this.minPixelPadding=0;this.chart=a;this.reversed=d.reversed;this.zoomEnabled=d.zoomEnabled!==!1;this.categories=d.categories||e==="category";this.isLog=e==="logarithmic";this.isDatetimeAxis=e==="datetime";this.isLinked=v(d.linkedTo);this.tickmarkOffset=this.categories&&d.tickmarkPlacement==="between"?0.5:0;this.ticks={};this.minorTicks={};this.plotLinesAndBands=[];this.alternateBands={};this.len=0;this.minRange=this.userMinRange=d.minRange||d.maxZoom;this.range=d.range;this.offset=d.offset||0; +this.stacks={};this._stacksTouched=0;this.min=this.max=null;var f,d=this.options.events;va(this,a.axes)===-1&&(a.axes.push(this),a[c?"xAxis":"yAxis"].push(this));this.series=this.series||[];if(a.inverted&&c&&this.reversed===w)this.reversed=!0;this.removePlotLine=this.removePlotBand=this.removePlotBandOrLine;for(f in d)F(this,f,d[f]);if(this.isLog)this.val2lin=ra,this.lin2val=la},setOptions:function(a){this.options=z(this.defaultOptions,this.isXAxis?{}:this.defaultYAxisOptions,[this.defaultTopAxisOptions, +this.defaultRightAxisOptions,this.defaultBottomAxisOptions,this.defaultLeftAxisOptions][this.side],z(M[this.isXAxis?"xAxis":"yAxis"],a))},update:function(a,b){var c=this.chart,a=c.options[this.xOrY+"Axis"][this.options.index]=z(this.userOptions,a);this.destroy();this._addedPlotLB=!1;this.init(c,a);c.isDirtyBox=!0;p(b,!0)&&c.redraw()},remove:function(a){var b=this.chart,c=this.xOrY+"Axis";n(this.series,function(a){a.remove(!1)});na(b.axes,this);na(b[c],this);b.options[c].splice(this.options.index, +1);n(b[c],function(a,b){a.options.index=b});this.destroy();b.isDirtyBox=!0;p(a,!0)&&b.redraw()},defaultLabelFormatter:function(){var a=this.axis,b=this.value,c=a.categories,d=this.dateTimeLabelFormat,e=M.lang.numericSymbols,f=e&&e.length,g,h=a.options.labels.format,a=a.isLog?b:a.tickInterval;if(h)g=La(h,this);else if(c)g=b;else if(d)g=ya(d,b);else if(f&&a>=1E3)for(;f--&&g===w;)c=Math.pow(1E3,f+1),a>=c&&e[f]!==null&&(g=xa(b/c,-1)+e[f]);g===w&&(g=b>=1E3?xa(b,0):xa(b,-1));return g},getSeriesExtremes:function(){var a= +this,b=a.chart,c=a.stacks,d=[],e=[],f=a._stacksTouched+=1,g,h;a.hasVisibleSeries=!1;a.dataMin=a.dataMax=null;n(a.series,function(g){if(g.visible||!b.options.chart.ignoreHiddenSeries){var j=g.options,k,m,l,o,q,n,u,s,x,N=j.threshold,fa,E=[],B=0;a.hasVisibleSeries=!0;if(a.isLog&&N<=0)N=j.threshold=null;if(a.isXAxis){if(j=g.xData,j.length)a.dataMin=A(p(a.dataMin,j[0]),Pa(j)),a.dataMax=t(p(a.dataMax,j[0]),ua(j))}else{var T,r,J,z=g.cropped,y=g.xAxis.getExtremes(),C=!!g.modifyValue;k=j.stacking;a.usePercentage= +k==="percent";if(k)q=j.stack,o=g.type+p(q,""),n="-"+o,g.stackKey=o,m=d[o]||[],d[o]=m,l=e[n]||[],e[n]=l;if(a.usePercentage)a.dataMin=0,a.dataMax=99;j=g.processedXData;u=g.processedYData;fa=u.length;for(h=0;h0))if(C&&(x=g.modifyValue(x)),g.getExtremesFromAll|| +z||(j[h+1]||s)>=y.min&&(j[h-1]||s)<=y.max)if(s=x.length)for(;s--;)x[s]!==null&&(E[B++]=x[s]);else E[B++]=x}if(!a.usePercentage&&E.length)g.dataMin=k=Pa(E),g.dataMax=g=ua(E),a.dataMin=A(p(a.dataMin,k),k),a.dataMax=t(p(a.dataMax,g),g);if(v(N))if(a.dataMin>=N)a.dataMin=N,a.ignoreMinPadding=!0;else if(a.dataMaxf+this.width)l=!0}else if(c=f,i=m-this.right,hg+this.height)l=!0;return l&&!d?null:e.renderer.crispLine(["M",c,h,"L",i,j],b||0)},getPlotBandPath:function(a,b){var c=this.getPlotLinePath(b),d=this.getPlotLinePath(a);d&&c?d.push(c[4], +c[5],c[1],c[2]):d=null;return d},getLinearTickPositions:function(a,b,c){for(var d,b=oa(W(b/a)*a),c=oa(pa(c/a)*a),e=[];b<=c;){e.push(b);b=oa(b+a);if(b===d)break;d=b}return e},getLogTickPositions:function(a,b,c,d){var e=this.options,f=this.len,g=[];if(!d)this._minorAutoInterval=null;if(a>=0.5)a=r(a),g=this.getLinearTickPositions(a,b,c);else if(a>=0.08)for(var f=W(b),h,i,j,k,m,e=a>0.3?[1,2,4]:a>0.15?[1,2,4,6,8]:[1,2,3,4,5,6,7,8,9];fb&&(!d|| +k<=c)&&g.push(k),k>c&&(m=!0),k=j}else if(b=la(b),c=la(c),a=e[d?"minorTickInterval":"tickInterval"],a=p(a==="auto"?null:a,this._minorAutoInterval,(c-b)*(e.tickPixelInterval/(d?5:1))/((d?f/this.tickPositions.length:f)||1)),a=yb(a,null,P.pow(10,W(P.log(a)/P.LN10))),g=Fa(this.getLinearTickPositions(a,b,c),ra),!d)this._minorAutoInterval=a/5;if(!d)this.tickInterval=a;return g},getMinorTickPositions:function(){var a=this.options,b=this.tickPositions,c=this.minorTickInterval,d=[],e;if(this.isLog){e=b.length; +for(a=1;a=this.minRange,f,g,h,i,j;if(this.isXAxis&&this.minRange===w&&!this.isLog)v(a.min)||v(a.max)?this.minRange=null:(n(this.series, +function(a){i=a.xData;for(g=j=a.xIncrement?1:i.length-1;g>0;g--)if(h=i[g]-i[g-1],f===w||hb&&(g=0);c=t(c,g);e=t(e,h?0:g/2);f=t(f,h==="on"?0:g);!a.noSharedTooltip&&v(m)&&(d=v(d)?A(d,m):m)}),g=this.ordinalSlope&&d?this.ordinalSlope/d:1,this.minPointOffset=e*=g,this.pointRangePadding=f*=g,this.pointRange=A(c,b),this.closestPointRange=d;if(a)this.oldTransA=h;this.translationSlope=this.transA=h=this.len/(b+f||1);this.transB=this.horiz?this.left:this.bottom;this.minPixelPadding= +h*e},setTickPositions:function(a){var b=this,c=b.chart,d=b.options,e=b.isLog,f=b.isDatetimeAxis,g=b.isXAxis,h=b.isLinked,i=b.options.tickPositioner,j=d.maxPadding,k=d.minPadding,m=d.tickInterval,l=d.minTickInterval,o=d.tickPixelInterval,q=b.categories;h?(b.linkedParent=c[g?"xAxis":"yAxis"][d.linkedTo],c=b.linkedParent.getExtremes(),b.min=p(c.min,c.dataMin),b.max=p(c.max,c.dataMax),d.type!==b.linkedParent.options.type&&Ba(11,1)):(b.min=p(b.userMin,d.min,b.dataMin),b.max=p(b.userMax,d.max,b.dataMax)); +if(e)!a&&A(b.min,p(b.dataMin,b.min))<=0&&Ba(10,1),b.min=oa(ra(b.min)),b.max=oa(ra(b.max));if(b.range&&(b.userMin=b.min=t(b.min,b.max-b.range),b.userMax=b.max,a))b.range=null;b.beforePadding&&b.beforePadding();b.adjustForMinRange();if(!q&&!b.usePercentage&&!h&&v(b.min)&&v(b.max)&&(c=b.max-b.min)){if(!v(d.min)&&!v(b.userMin)&&k&&(b.dataMin<0||!b.ignoreMinPadding))b.min-=c*k;if(!v(d.max)&&!v(b.userMax)&&j&&(b.dataMax>0||!b.ignoreMaxPadding))b.max+=c*j}b.tickInterval=b.min===b.max||b.min===void 0||b.max=== +void 0?1:h&&!m&&o===b.linkedParent.options.tickPixelInterval?b.linkedParent.tickInterval:p(m,q?1:(b.max-b.min)*o/(b.len||1));g&&!a&&n(b.series,function(a){a.processData(b.min!==b.oldMin||b.max!==b.oldMax)});b.setAxisTranslation(!0);b.beforeSetTickPositions&&b.beforeSetTickPositions();if(b.postProcessTickInterval)b.tickInterval=b.postProcessTickInterval(b.tickInterval);if(!m&&b.tickIntervale&&i.shift(),d.endOnTick?b.max=f:b.max+h(b[d]||0)&&this.options.alignTicks!==!1)b[d]=c.length;a.maxTicks=b},adjustTickAmount:function(){var a=this._maxTicksKey,b=this.tickPositions,c=this.chart.maxTicks;if(c&&c[a]&&!this.isDatetimeAxis&& +!this.categories&&!this.isLinked&&this.options.alignTicks!==!1){var d=this.tickAmount,e=b.length;this.tickAmount=a=c[a];if(e=this.dataMax&&(b=w));this.displayBtn=a!==w||b!==w;this.setExtremes(a,b,!1,w,{trigger:"zoom"});return!0},setAxisSize:function(){var a=this.chart,b=this.options,c=b.offsetLeft||0,d=b.offsetRight||0,e=this.horiz,f,g;this.left=g=p(b.left,a.plotLeft+c);this.top=f=p(b.top,a.plotTop);this.width=c=p(b.width,a.plotWidth-c+d);this.height=b=p(b.height,a.plotHeight); +this.bottom=a.chartHeight-b-f;this.right=a.chartWidth-c-g;this.len=t(e?c:b,0);this.pos=e?g:f},getExtremes:function(){var a=this.isLog;return{min:a?oa(la(this.min)):this.min,max:a?oa(la(this.max)):this.max,dataMin:this.dataMin,dataMax:this.dataMax,userMin:this.userMin,userMax:this.userMax}},getThreshold:function(a){var b=this.isLog,c=b?la(this.min):this.min,b=b?la(this.max):this.max;c>a||a===null?a=c:b=a.min&&b<=a.max)j[b]||(j[b]=new ab(a,b)),s&&j[b].isNew&&j[b].render(c,!0),j[b].render(c,!1,1)}),q&&a.min===0&&(j[-1]||(j[-1]=new ab(a,-1,null,!0)),j[-1].render(-1))), +o&&n(g,function(b,c){if(c%2===0&&b1||U(b-f.y)>1))clearTimeout(this.tooltipTimeout),this.tooltipTimeout=setTimeout(function(){e&&e.move(a,b,c,d)},32)},hide:function(){var a=this,b;clearTimeout(this.hideTimer);if(!this.isHidden)b=this.chart.hoverPoints,this.hideTimer=setTimeout(function(){a.label.fadeOut();a.isHidden=!0},p(this.options.hideDelay,500)),b&&n(b,function(a){a.setState()}),this.chart.hoverPoints=null},hideCrosshairs:function(){n(this.crosshairs,function(a){a&&a.hide()})},getAnchor:function(a,b){var c,d=this.chart,e= +d.inverted,f=d.plotTop,g=0,h=0,i,a=ja(a);c=a[0].tooltipPos;this.followPointer&&b&&(b.chartX===w&&(b=d.pointer.normalize(b)),c=[b.chartX-d.plotLeft,b.chartY-f]);c||(n(a,function(a){i=a.series.yAxis;g+=a.plotX;h+=(a.plotLow?(a.plotLow+a.plotHigh)/2:a.plotY)+(!e&&i?i.top-f:0)}),g/=a.length,h/=a.length,c=[e?d.plotWidth-h:g,this.shared&&!e&&a.length>1&&b?b.chartY-f:e?d.plotHeight-g:h]);return Fa(c,r)},getPosition:function(a,b,c){var d=this.chart,e=d.plotLeft,f=d.plotTop,g=d.plotWidth,h=d.plotHeight,i= +p(this.options.distance,12),j=c.plotX,c=c.plotY,d=j+e+(d.inverted?i:-a-i),k=c-b+f+15,m;d<7&&(d=e+t(j,0)+i);d+a>e+g&&(d-=d+a-(e+g),k=c-b+f-i,m=!0);k=k&&c<=k+b&&(k=c+f+i));k+b>f+h&&(k=t(f,f+h-b-i));return{x:d,y:k}},defaultFormatter:function(a){var b=this.points||ja(this),c=b[0].series,d;d=[c.tooltipHeaderFormatter(b[0])];n(b,function(a){c=a.series;d.push(c.tooltipFormatter&&c.tooltipFormatter(a)||a.point.tooltipFormatter(c.tooltipOptions.pointFormat))});d.push(a.options.footerFormat|| +"");return d.join("")},refresh:function(a,b){var c=this.chart,d=this.label,e=this.options,f,g,h,i={},j,k=[];j=e.formatter||this.defaultFormatter;var i=c.hoverPoints,m,l=e.crosshairs;h=this.shared;clearTimeout(this.hideTimer);this.followPointer=ja(a)[0].series.tooltipOptions.followPointer;g=this.getAnchor(a,b);f=g[0];g=g[1];h&&(!a.series||!a.series.noSharedTooltip)?(c.hoverPoints=a,i&&n(i,function(a){a.setState()}),n(a,function(a){a.setState("hover");k.push(a.getLabelConfig())}),i={x:a[0].category, +y:a[0].y},i.points=k,a=a[0]):i=a.getLabelConfig();j=j.call(i,this);i=a.series;h=h||!i.isCartesian||i.tooltipOutsidePlot||c.isInsidePlot(f,g);j===!1||!h?this.hide():(this.isHidden&&(hb(d),d.attr("opacity",1).show()),d.attr({text:j}),m=e.borderColor||a.color||i.color||"#606060",d.attr({stroke:m}),this.updatePosition({plotX:f,plotY:g}),this.isHidden=!1);if(l){l=ja(l);for(d=l.length;d--;)if(e=a.series[d?"yAxis":"xAxis"],l[d]&&e)if(h=d?p(a.stackY,a.y):a.x,e.isLog&&(h=ra(h)),e=e.getPlotLinePath(h,1),this.crosshairs[d])this.crosshairs[d].attr({d:e, +visibility:"visible"});else{h={"stroke-width":l[d].width||1,stroke:l[d].color||"#C0C0C0",zIndex:l[d].zIndex||2};if(l[d].dashStyle)h.dashstyle=l[d].dashStyle;this.crosshairs[d]=c.renderer.path(e).attr(h).add()}}K(c,"tooltipRefresh",{text:j,x:f+c.plotLeft,y:g+c.plotTop,borderColor:m})},updatePosition:function(a){var b=this.chart,c=this.label,c=(this.options.positioner||this.getPosition).call(this,c.width,c.height,a);this.move(r(c.x),r(c.y),a.plotX+b.plotLeft,a.plotY+b.plotTop)}};pb.prototype={init:function(a, +b){var c=ia?"":b.chart.zoomType,d=a.inverted,e;this.options=b;this.chart=a;this.zoomX=e=/x/.test(c);this.zoomY=c=/y/.test(c);this.zoomHor=e&&!d||c&&d;this.zoomVert=c&&!d||e&&d;this.pinchDown=[];this.lastValidTouch={};if(b.tooltip.enabled)a.tooltip=new Gb(a,b.tooltip);this.setDOMEvents()},normalize:function(a){var b,c,d,a=a||Y.event;if(!a.target)a.target=a.srcElement;a=Zb(a);d=a.touches?a.touches.item(0):a;this.chartPosition=b=ec(this.chart.container);d.pageX===w?(c=a.x,b=a.y):(c=d.pageX-b.left,b= +d.pageY-b.top);return y(a,{chartX:r(c),chartY:r(b)})},getCoordinates:function(a){var b={xAxis:[],yAxis:[]};n(this.chart.axes,function(c){b[c.isXAxis?"xAxis":"yAxis"].push({axis:c,value:c.toValue(a[c.horiz?"chartX":"chartY"])})});return b},getIndex:function(a){var b=this.chart;return b.inverted?b.plotHeight+b.plotTop-a.chartY:a.chartX-b.plotLeft},runPointActions:function(a){var b=this.chart,c=b.series,d=b.tooltip,e,f=b.hoverPoint,g=b.hoverSeries,h,i,j=b.chartWidth,k=this.getIndex(a);if(d&&this.options.tooltip.shared&& +(!g||!g.noSharedTooltip)){e=[];h=c.length;for(i=0;ij&&e.splice(h,1);if(e.length&&e[0].clientX!==this.hoverX)d.refresh(e,a),this.hoverX=e[0].clientX}if(g&&g.tracker){if((b=g.tooltipPoints[k])&&b!==f)b.onMouseOver(a)}else d&&d.followPointer&&!d.isHidden&&(a=d.getAnchor([{}],a), +d.updatePosition({plotX:a[0],plotY:a[1]}))},reset:function(a){var b=this.chart,c=b.hoverSeries,d=b.hoverPoint,e=b.tooltip,b=e&&e.shared?b.hoverPoints:d;(a=a&&e&&b)&&ja(b)[0].plotX===w&&(a=!1);if(a)e.refresh(b);else{if(d)d.onMouseOut();if(c)c.onMouseOut();e&&(e.hide(),e.hideCrosshairs());this.hoverX=null}},scaleGroups:function(a,b){var c=this.chart;n(c.series,function(d){d.xAxis&&d.xAxis.zoomEnabled&&(d.group.attr(a),d.markerGroup&&(d.markerGroup.attr(a),d.markerGroup.clip(b?c.clipRect:null)),d.dataLabelsGroup&& +d.dataLabelsGroup.attr(a))});c.clipRect.attr(b||c.clipBox)},pinchTranslateDirection:function(a,b,c,d,e,f,g){var h=this.chart,i=a?"x":"y",j=a?"X":"Y",k="chart"+j,m=a?"width":"height",l=h["plot"+(a?"Left":"Top")],o,q,n=1,p=h.inverted,s=h.bounds[a?"h":"v"],x=b.length===1,r=b[0][k],t=c[0][k],E=!x&&b[1][k],B=!x&&c[1][k],v,c=function(){!x&&U(r-E)>20&&(n=U(t-B)/U(r-E));q=(l-t)/n+r;o=h["plot"+(a?"Width":"Height")]/n};c();b=q;bs.max&&(b=s.max-o,v=!0);v?(t-=0.8*(t-g[i][0]),x||(B-= +0.8*(B-g[i][1])),c()):g[i]=[t,B];p||(f[i]=q-l,f[m]=o);f=p?1/n:n;e[m]=o;e[i]=b;d[p?a?"scaleY":"scaleX":"scale"+j]=n;d["translate"+j]=f*l+(t-f*r)},pinch:function(a){var b=this,c=b.chart,d=b.pinchDown,e=c.tooltip&&c.tooltip.options.followTouchMove,f=a.touches,g=f.length,h=b.lastValidTouch,i=b.zoomHor||b.pinchHor,j=b.zoomVert||b.pinchVert,k=i||j,m=b.selectionMarker,l={},o={};a.type==="touchstart"&&(e||k)&&a.preventDefault();Fa(f,function(a){return b.normalize(a)});if(a.type==="touchstart")n(f,function(a, +b){d[b]={chartX:a.chartX,chartY:a.chartY}}),h.x=[d[0].chartX,d[1]&&d[1].chartX],h.y=[d[0].chartY,d[1]&&d[1].chartY],n(c.axes,function(a){if(a.zoomEnabled){var b=c.bounds[a.horiz?"h":"v"],d=a.minPixelPadding,e=a.toPixels(a.dataMin),f=a.toPixels(a.dataMax),g=A(e,f),e=t(e,f);b.min=A(a.pos,g-d);b.max=t(a.pos+a.len,e+d)}});else if(d.length){if(!m)b.selectionMarker=m=y({destroy:qa},c.plotBox);i&&b.pinchTranslateDirection(!0,d,f,l,m,o,h);j&&b.pinchTranslateDirection(!1,d,f,l,m,o,h);b.hasPinched=k;b.scaleGroups(l, +o);!k&&e&&g===1&&this.runPointActions(b.normalize(a))}},dragStart:function(a){var b=this.chart;b.mouseIsDown=a.type;b.cancelClick=!1;b.mouseDownX=this.mouseDownX=a.chartX;this.mouseDownY=a.chartY},drag:function(a){var b=this.chart,c=b.options.chart,d=a.chartX,a=a.chartY,e=this.zoomHor,f=this.zoomVert,g=b.plotLeft,h=b.plotTop,i=b.plotWidth,j=b.plotHeight,k,m=this.mouseDownX,l=this.mouseDownY;dg+i&&(d=g+i);ah+j&&(a=h+j);this.hasDragged=Math.sqrt(Math.pow(m-d,2)+Math.pow(l-a,2));if(this.hasDragged> +10){k=b.isInsidePlot(m-g,l-h);if(b.hasCartesianSeries&&(this.zoomX||this.zoomY)&&k&&!this.selectionMarker)this.selectionMarker=b.renderer.rect(g,h,e?1:i,f?1:j,0).attr({fill:c.selectionMarkerFill||"rgba(69,114,167,0.25)",zIndex:7}).add();this.selectionMarker&&e&&(e=d-m,this.selectionMarker.attr({width:U(e),x:(e>0?0:e)+m}));this.selectionMarker&&f&&(e=a-l,this.selectionMarker.attr({height:U(e),y:(e>0?0:e)+l}));k&&!this.selectionMarker&&c.panning&&b.pan(d)}},drop:function(a){var b=this.chart,c=this.hasPinched; +if(this.selectionMarker){var d={xAxis:[],yAxis:[],originalEvent:a.originalEvent||a},e=this.selectionMarker,f=e.x,g=e.y,h;if(this.hasDragged||c)n(b.axes,function(a){if(a.zoomEnabled){var b=a.horiz,c=a.toValue(b?f:g),b=a.toValue(b?f+e.width:g+e.height);!isNaN(c)&&!isNaN(b)&&(d[a.xOrY+"Axis"].push({axis:a,min:A(c,b),max:t(c,b)}),h=!0)}}),h&&K(b,"selection",d,function(a){b.zoom(y(a,c?{animation:!1}:null))});this.selectionMarker=this.selectionMarker.destroy();c&&this.scaleGroups({translateX:b.plotLeft, +translateY:b.plotTop,scaleX:1,scaleY:1})}if(b)L(b.container,{cursor:b._cursor}),b.cancelClick=this.hasDragged>10,b.mouseIsDown=this.hasDragged=this.hasPinched=!1,this.pinchDown=[]},onContainerMouseDown:function(a){a=this.normalize(a);a.preventDefault&&a.preventDefault();this.dragStart(a)},onDocumentMouseUp:function(a){this.drop(a)},onDocumentMouseMove:function(a){var b=this.chart,c=this.chartPosition,d=b.hoverSeries,a=Zb(a);c&&d&&d.isCartesian&&!b.isInsidePlot(a.pageX-c.left-b.plotLeft,a.pageY-c.top- +b.plotTop)&&this.reset()},onContainerMouseLeave:function(){this.reset();this.chartPosition=null},onContainerMouseMove:function(a){var b=this.chart,a=this.normalize(a);a.returnValue=!1;b.mouseIsDown==="mousedown"&&this.drag(a);b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&!b.openMenu&&this.runPointActions(a)},inClass:function(a,b){for(var c;a;){if(c=G(a,"class"))if(c.indexOf(b)!==-1)return!0;else if(c.indexOf("highcharts-container")!==-1)return!1;a=a.parentNode}},onTrackerMouseOut:function(a){var b= +this.chart.hoverSeries;if(b&&!b.options.stickyTracking&&!this.inClass(a.toElement||a.relatedTarget,"highcharts-tooltip"))b.onMouseOut()},onContainerClick:function(a){var b=this.chart,c=b.hoverPoint,d=b.plotLeft,e=b.plotTop,f=b.inverted,g,h,i,a=this.normalize(a);a.cancelBubble=!0;if(!b.cancelClick)c&&this.inClass(a.target,"highcharts-tracker")?(g=this.chartPosition,h=c.plotX,i=c.plotY,y(c,{pageX:g.left+d+(f?b.plotWidth-i:h),pageY:g.top+e+(f?b.plotHeight-h:i)}),K(c.series,"click",y(a,{point:c})),b.hoverPoint&& +c.firePointEvent("click",a)):(y(a,this.getCoordinates(a)),b.isInsidePlot(a.chartX-d,a.chartY-e)&&K(b,"click",a))},onContainerTouchStart:function(a){var b=this.chart;a.touches.length===1?(a=this.normalize(a),b.isInsidePlot(a.chartX-b.plotLeft,a.chartY-b.plotTop)&&(this.runPointActions(a),this.pinch(a))):a.touches.length===2&&this.pinch(a)},onContainerTouchMove:function(a){(a.touches.length===1||a.touches.length===2)&&this.pinch(a)},onDocumentTouchEnd:function(a){this.drop(a)},setDOMEvents:function(){var a= +this,b=a.chart.container,c;this._events=c=[[b,"onmousedown","onContainerMouseDown"],[b,"onmousemove","onContainerMouseMove"],[b,"onclick","onContainerClick"],[b,"mouseleave","onContainerMouseLeave"],[H,"mousemove","onDocumentMouseMove"],[H,"mouseup","onDocumentMouseUp"]];gb&&c.push([b,"ontouchstart","onContainerTouchStart"],[b,"ontouchmove","onContainerTouchMove"],[H,"touchend","onDocumentTouchEnd"]);n(c,function(b){a["_"+b[2]]=function(c){a[b[2]](c)};b[1].indexOf("on")===0?b[0][b[1]]=a["_"+b[2]]: +F(b[0],b[1],a["_"+b[2]])})},destroy:function(){var a=this;n(a._events,function(b){b[1].indexOf("on")===0?b[0][b[1]]=null:X(b[0],b[1],a["_"+b[2]])});delete a._events;clearInterval(a.tooltipTimeout)}};Hb.prototype={init:function(a,b){var c=this,d=b.itemStyle,e=p(b.padding,8),f=b.itemMarginTop||0;this.options=b;if(b.enabled)c.baseline=C(d.fontSize)+3+f,c.itemStyle=d,c.itemHiddenStyle=z(d,b.itemHiddenStyle),c.itemMarginTop=f,c.padding=e,c.initialItemX=e,c.initialItemY=e-5,c.maxItemWidth=0,c.chart=a,c.itemHeight= +0,c.lastLineHeight=0,c.render(),F(c.chart,"endResize",function(){c.positionCheckboxes()})},colorizeItem:function(a,b){var c=this.options,d=a.legendItem,e=a.legendLine,f=a.legendSymbol,g=this.itemHiddenStyle.color,c=b?c.itemStyle.color:g,h=b?a.color:g,g=a.options&&a.options.marker,i={stroke:h,fill:h},j;d&&d.css({fill:c,color:c});e&&e.attr({stroke:h});if(f){if(g)for(j in g=a.convertAttribs(g),g)d=g[j],d!==w&&(i[j]=d);f.attr(i)}},positionItem:function(a){var b=this.options,c=b.symbolPadding,b=!b.rtl, +d=a._legendItemPos,e=d[0],d=d[1],f=a.checkbox;a.legendGroup&&a.legendGroup.translate(b?e:this.legendWidth-e-2*c-4,d);if(f)f.x=e,f.y=d},destroyItem:function(a){var b=a.checkbox;n(["legendItem","legendLine","legendSymbol","legendGroup"],function(b){a[b]&&a[b].destroy()});b&&Za(a.checkbox)},destroy:function(){var a=this.group,b=this.box;if(b)this.box=b.destroy();if(a)this.group=a.destroy()},positionCheckboxes:function(a){var b=this.group.alignAttr,c,d=this.clipHeight||this.legendHeight;if(b)c=b.translateY, +n(this.allItems,function(e){var f=e.checkbox,g;f&&(g=c+f.y+(a||0)+3,L(f,{left:b.translateX+e.legendItemWidth+f.x-20+"px",top:g+"px",display:g>c-6&&g(l||c.chartWidth-2*k-n))b.itemX=n,b.itemY+=q+b.lastLineHeight+o,b.lastLineHeight=0;b.maxItemWidth=t(b.maxItemWidth,e);b.lastItemY=q+b.itemY+o;b.lastLineHeight=t(g,b.lastLineHeight);a._legendItemPos=[b.itemX,b.itemY];f?b.itemX+=e:(b.itemY+=q+g+o,b.lastLineHeight= +g);b.offsetWidth=l||t(f?b.itemX-n:e,b.offsetWidth)},render:function(){var a=this,b=a.chart,c=b.renderer,d=a.group,e,f,g,h,i=a.box,j=a.options,k=a.padding,m=j.borderWidth,l=j.backgroundColor;a.itemX=a.initialItemX;a.itemY=a.initialItemY;a.offsetWidth=0;a.lastItemY=0;if(!d)a.group=d=c.g("legend").attr({zIndex:7}).add(),a.contentGroup=c.g().attr({zIndex:1}).add(d),a.scrollGroup=c.g().add(a.contentGroup);a.renderTitle();e=[];n(b.series,function(a){var b=a.options;b.showInLegend&&!v(b.linkedTo)&&(e=e.concat(a.legendItems|| +(b.legendType==="point"?a.data:a)))});Sb(e,function(a,b){return(a.options&&a.options.legendIndex||0)-(b.options&&b.options.legendIndex||0)});j.reversed&&e.reverse();a.allItems=e;a.display=f=!!e.length;n(e,function(b){a.renderItem(b)});g=j.width||a.offsetWidth;h=a.lastItemY+a.lastLineHeight+a.titleHeight;h=a.handleOverflow(h);if(m||l){g+=k;h+=k;if(i){if(g>0&&h>0)i[i.isNew?"attr":"animate"](i.crisp(null,null,null,g,h)),i.isNew=!1}else a.box=i=c.rect(0,0,g,h,j.borderRadius,m||0).attr({stroke:j.borderColor, +"stroke-width":m||0,fill:l||ba}).add(d).shadow(j.shadow),i.isNew=!0;i[f?"show":"hide"]()}a.legendWidth=g;a.legendHeight=h;n(e,function(b){a.positionItem(b)});f&&d.align(y({width:g,height:h},j),!0,"spacingBox");b.isResizing||this.positionCheckboxes()},handleOverflow:function(a){var b=this,c=this.chart,d=c.renderer,e=this.options,f=e.y,f=c.spacingBox.height+(e.verticalAlign==="top"?-f:f)-this.padding,g=e.maxHeight,h=this.clipRect,i=e.navigation,j=p(i.animation,!0),k=i.arrowSize||12,m=this.nav;e.layout=== +"horizontal"&&(f/=2);g&&(f=A(f,g));if(a>f&&!e.useHTML){this.clipHeight=c=f-20-this.titleHeight;this.pageCount=pa(a/c);this.currentPage=p(this.currentPage,1);this.fullHeight=a;if(!h)h=b.clipRect=d.clipRect(0,0,9999,0),b.contentGroup.clip(h);h.attr({height:c});if(!m)this.nav=m=d.g().attr({zIndex:1}).add(this.group),this.up=d.symbol("triangle",0,0,k,k).on("click",function(){b.scroll(-1,j)}).add(m),this.pager=d.text("",15,10).css(i.style).add(m),this.down=d.symbol("triangle-down",0,0,k,k).on("click", +function(){b.scroll(1,j)}).add(m);b.scroll(0);a=f}else if(m)h.attr({height:c.chartHeight}),m.hide(),this.scrollGroup.attr({translateY:1}),this.clipHeight=0;return a},scroll:function(a,b){var c=this.pageCount,d=this.currentPage+a,e=this.clipHeight,f=this.options.navigation,g=f.activeColor,h=f.inactiveColor,f=this.pager,i=this.padding;d>c&&(d=c);if(d>0)b!==w&&$a(b,this.chart),this.nav.attr({translateX:i,translateY:e+7+this.titleHeight,visibility:"visible"}),this.up.attr({fill:d===1?h:g}).css({cursor:d=== +1?"default":"pointer"}),f.attr({text:d+"/"+this.pageCount}),this.down.attr({x:18+this.pager.getBBox().width,fill:d===c?h:g}).css({cursor:d===c?"default":"pointer"}),e=-A(e*(d-1),this.fullHeight-e+i)+1,this.scrollGroup.animate({translateY:e}),f.attr({text:d+"/"+c}),this.currentPage=d,this.positionCheckboxes(e)}};Sa.prototype={init:function(a,b){var c,d=a.series;a.series=null;c=z(M,a);c.series=a.series=d;var d=c.chart,e=d.margin,e=da(e)?e:[e,e,e,e];this.optionsMarginTop=p(d.marginTop,e[0]);this.optionsMarginRight= +p(d.marginRight,e[1]);this.optionsMarginBottom=p(d.marginBottom,e[2]);this.optionsMarginLeft=p(d.marginLeft,e[3]);this.runChartClick=(e=d.events)&&!!e.click;this.bounds={h:{},v:{}};this.callback=b;this.isResizing=0;this.options=c;this.axes=[];this.series=[];this.hasCartesianSeries=d.showAxes;var f=this,g;f.index=Ua.length;Ua.push(f);d.reflow!==!1&&F(f,"load",function(){f.initReflow()});if(e)for(g in e)F(f,g,e[g]);f.xAxis=[];f.yAxis=[];f.animation=ia?!1:p(d.animation,!0);f.pointCount=0;f.counters= +new Rb;f.firstRender()},initSeries:function(a){var b=this.options.chart;(b=Q[a.type||b.type||b.defaultSeriesType])||Ba(17,!0);b=new b;b.init(this,a);return b},addSeries:function(a,b,c){var d,e=this;a&&(b=p(b,!0),K(e,"addSeries",{options:a},function(){d=e.initSeries(a);e.isDirtyLegend=!0;b&&e.redraw(c)}));return d},addAxis:function(a,b,c,d){var b=b?"xAxis":"yAxis",e=this.options;new Da(this,z(a,{index:this[b].length}));e[b]=ja(e[b]||{});e[b].push(a);p(c,!0)&&this.redraw(d)},isInsidePlot:function(a, +b,c){var d=c?b:a,a=c?a:b;return d>=0&&d<=this.plotWidth&&a>=0&&a<=this.plotHeight},adjustTickAmounts:function(){this.options.chart.alignTicks!==!1&&n(this.axes,function(a){a.adjustTickAmount()});this.maxTicks=null},redraw:function(a){var b=this.axes,c=this.series,d=this.pointer,e=this.legend,f=this.isDirtyLegend,g,h=this.isDirtyBox,i=c.length,j=i,k=this.renderer,m=k.isHidden(),l=[];$a(a,this);for(m&&this.cloneRenderTo();j--;)if(a=c[j],a.isDirty&&a.options.stacking){g=!0;break}if(g)for(j=i;j--;)if(a= +c[j],a.options.stacking)a.isDirty=!0;n(c,function(a){a.isDirty&&a.options.legendType==="point"&&(f=!0)});if(f&&e.options.enabled)e.render(),this.isDirtyLegend=!1;if(this.hasCartesianSeries){if(!this.isResizing)this.maxTicks=null,n(b,function(a){a.setScale()});this.adjustTickAmounts();this.getMargins();n(b,function(a){if(a.isDirtyExtremes)a.isDirtyExtremes=!1,l.push(function(){K(a,"afterSetExtremes",a.getExtremes())});if(a.isDirty||h||g)a.redraw(),h=!0})}h&&this.drawChartBox();n(c,function(a){a.isDirty&& +a.visible&&(!a.isCartesian||a.xAxis)&&a.redraw()});d&&d.reset&&d.reset(!0);k.draw();K(this,"redraw");m&&this.cloneRenderTo(!0);n(l,function(a){a.call()})},showLoading:function(a){var b=this.options,c=this.loadingDiv,d=b.loading;if(!c)this.loadingDiv=c=aa(Qa,{className:"highcharts-loading"},y(d.style,{zIndex:10,display:ba}),this.container),this.loadingSpan=aa("span",null,d.labelStyle,c);this.loadingSpan.innerHTML=a||b.lang.loading;if(!this.loadingShown)L(c,{opacity:0,display:"",left:this.plotLeft+ +"px",top:this.plotTop+"px",width:this.plotWidth+"px",height:this.plotHeight+"px"}),Mb(c,{opacity:d.style.opacity},{duration:d.showDuration||0}),this.loadingShown=!0},hideLoading:function(){var a=this.options,b=this.loadingDiv;b&&Mb(b,{opacity:0},{duration:a.loading.hideDuration||100,complete:function(){L(b,{display:ba})}});this.loadingShown=!1},get:function(a){var b=this.axes,c=this.series,d,e;for(d=0;dA(e.dataMin,e.min)&&c19?this.containerHeight:400))},cloneRenderTo:function(a){var b=this.renderToClone,c=this.container;a?b&&(this.renderTo.appendChild(c),Za(b),delete this.renderToClone): +(c&&this.renderTo.removeChild(c),this.renderToClone=b=this.renderTo.cloneNode(0),L(b,{position:"absolute",top:"-9999px",display:"block"}),H.body.appendChild(b),c&&b.appendChild(c))},getContainer:function(){var a,b=this.options.chart,c,d,e;this.renderTo=a=b.renderTo;e="highcharts-"+Kb++;if(ma(a))this.renderTo=a=H.getElementById(a);a||Ba(13,!0);c=C(G(a,"data-highcharts-chart"));!isNaN(c)&&Ua[c]&&Ua[c].destroy();G(a,"data-highcharts-chart",this.index);a.innerHTML="";a.offsetWidth||this.cloneRenderTo(); +this.getChartSize();c=this.chartWidth;d=this.chartHeight;this.container=a=aa(Qa,{className:"highcharts-container"+(b.className?" "+b.className:""),id:e},y({position:"relative",overflow:"hidden",width:c+"px",height:d+"px",textAlign:"left",lineHeight:"normal",zIndex:0,"-webkit-tap-highlight-color":"rgba(0,0,0,0)"},b.style),this.renderToClone||a);this._cursor=a.style.cursor;this.renderer=b.forExport?new Ga(a,c,d,!0):new cb(a,c,d);ia&&this.renderer.create(this,a,c,d)},getMargins:function(){var a=this.options.chart, +b=a.spacingTop,c=a.spacingRight,d=a.spacingBottom,a=a.spacingLeft,e,f=this.legend,g=this.optionsMarginTop,h=this.optionsMarginLeft,i=this.optionsMarginRight,j=this.optionsMarginBottom,k=this.options.title,m=this.options.subtitle,l=this.options.legend,o=p(l.margin,10),q=l.x,O=l.y,u=l.align,s=l.verticalAlign;this.resetMargins();e=this.axisOffset;if((this.title||this.subtitle)&&!v(this.optionsMarginTop))if(m=t(this.title&&!k.floating&&!k.verticalAlign&&k.y||0,this.subtitle&&!m.floating&&!m.verticalAlign&& +m.y||0))this.plotTop=t(this.plotTop,m+p(k.margin,15)+b);if(f.display&&!l.floating)if(u==="right"){if(!v(i))this.marginRight=t(this.marginRight,f.legendWidth-q+o+c)}else if(u==="left"){if(!v(h))this.plotLeft=t(this.plotLeft,f.legendWidth+q+o+a)}else if(s==="top"){if(!v(g))this.plotTop=t(this.plotTop,f.legendHeight+O+o+b)}else if(s==="bottom"&&!v(j))this.marginBottom=t(this.marginBottom,f.legendHeight-O+o+d);this.extraBottomMargin&&(this.marginBottom+=this.extraBottomMargin);this.extraTopMargin&&(this.plotTop+= +this.extraTopMargin);this.hasCartesianSeries&&n(this.axes,function(a){a.getOffset()});v(h)||(this.plotLeft+=e[3]);v(g)||(this.plotTop+=e[0]);v(j)||(this.marginBottom+=e[2]);v(i)||(this.marginRight+=e[1]);this.setChartSize()},initReflow:function(){function a(a){var g=c.width||vb(d,"width"),h=c.height||vb(d,"height"),a=a?a.target:Y;if(!b.hasUserSize&&g&&h&&(a===Y||a===H)){if(g!==b.containerWidth||h!==b.containerHeight)clearTimeout(e),b.reflowTimeout=e=setTimeout(function(){if(b.container)b.setSize(g, +h,!1),b.hasUserSize=null},100);b.containerWidth=g;b.containerHeight=h}}var b=this,c=b.options.chart,d=b.renderTo,e;F(Y,"resize",a);F(b,"destroy",function(){X(Y,"resize",a)})},setSize:function(a,b,c){var d=this,e,f,g;d.isResizing+=1;g=function(){d&&K(d,"endResize",null,function(){d.isResizing-=1})};$a(c,d);d.oldChartHeight=d.chartHeight;d.oldChartWidth=d.chartWidth;if(v(a))d.chartWidth=e=t(0,r(a)),d.hasUserSize=!!e;if(v(b))d.chartHeight=f=t(0,r(b));L(d.container,{width:e+"px",height:f+"px"});d.setChartSize(!0); +d.renderer.setSize(e,f,c);d.maxTicks=null;n(d.axes,function(a){a.isDirty=!0;a.setScale()});n(d.series,function(a){a.isDirty=!0});d.isDirtyLegend=!0;d.isDirtyBox=!0;d.getMargins();d.redraw(c);d.oldChartHeight=null;K(d,"resize");Ra===!1?g():setTimeout(g,Ra&&Ra.duration||500)},setChartSize:function(a){var b=this.inverted,c=this.renderer,d=this.chartWidth,e=this.chartHeight,f=this.options.chart,g=f.spacingTop,h=f.spacingRight,i=f.spacingBottom,j=f.spacingLeft,k=this.clipOffset,m,l,o,q;this.plotLeft=m= +r(this.plotLeft);this.plotTop=l=r(this.plotTop);this.plotWidth=o=t(0,r(d-m-this.marginRight));this.plotHeight=q=t(0,r(e-l-this.marginBottom));this.plotSizeX=b?q:o;this.plotSizeY=b?o:q;this.plotBorderWidth=b=f.plotBorderWidth||0;this.spacingBox=c.spacingBox={x:j,y:g,width:d-j-h,height:e-g-i};this.plotBox=c.plotBox={x:m,y:l,width:o,height:q};c=pa(t(b,k[3])/2);d=pa(t(b,k[0])/2);this.clipBox={x:c,y:d,width:W(this.plotSizeX-t(b,k[1])/2-c),height:W(this.plotSizeY-t(b,k[2])/2-d)};a||n(this.axes,function(a){a.setAxisSize(); +a.setAxisTranslation()})},resetMargins:function(){var a=this.options.chart,b=a.spacingRight,c=a.spacingBottom,d=a.spacingLeft;this.plotTop=p(this.optionsMarginTop,a.spacingTop);this.marginRight=p(this.optionsMarginRight,b);this.marginBottom=p(this.optionsMarginBottom,c);this.plotLeft=p(this.optionsMarginLeft,d);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:function(){var a=this.options.chart,b=this.renderer,c=this.chartWidth,d=this.chartHeight,e=this.chartBackground,f=this.plotBackground, +g=this.plotBorder,h=this.plotBGImage,i=a.borderWidth||0,j=a.backgroundColor,k=a.plotBackgroundColor,m=a.plotBackgroundImage,l=a.plotBorderWidth||0,o,q=this.plotLeft,n=this.plotTop,p=this.plotWidth,s=this.plotHeight,x=this.plotBox,r=this.clipRect,t=this.clipBox;o=i+(a.shadow?8:0);if(i||j)if(e)e.animate(e.crisp(null,null,null,c-o,d-o));else{e={fill:j||ba};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(o/2,o/2,c-o,d-o,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f? +f.animate(x):this.plotBackground=b.rect(q,n,p,s,0).attr({fill:k}).add().shadow(a.plotShadow);if(m)h?h.animate(x):this.plotBGImage=b.image(m,q,n,p,s).add();r?r.animate({width:t.width,height:t.height}):this.clipRect=b.clipRect(t);if(l)g?g.animate(g.crisp(null,q,n,p,s)):this.plotBorder=b.rect(q,n,p,s,0,l).attr({stroke:a.plotBorderColor,"stroke-width":l,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"], +function(g){c=Q[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=Q[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new Hb(a,d.legend);n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()}); +if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();a.render()});e.items&&n(e.items,function(b){var d=y(e.style,b.style),f=C(d.left)+a.plotLeft,g=C(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position); +a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;K(a,"destroy");Ua[a.index]=w;a.renderTo.removeAttribute("data-highcharts-chart");X(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())}); +if(d)d.innerHTML="",X(d),f&&Za(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!ca&&Y==Y.top&&H.readyState!=="complete"||ia&&!Y.canvg?(ia?$b.push(function(){a.firstRender()},a.options.global.canvasToolsURL):H.attachEvent("onreadystatechange",function(){H.detachEvent("onreadystatechange",a.firstRender);H.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),K(a,"init"),a.resetMargins(), +a.setChartSize(),a.propFromSeries(),a.getAxes(),n(b.series||[],function(b){a.initSeries(b)}),K(a,"beforeRender"),a.pointer=new pb(a,b),a.render(),a.renderer.draw(),c&&c.apply(a,[a]),n(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),K(a,"load")}};Sa.prototype.callbacks=[];var Ha=function(){};Ha.prototype={init:function(a,b,c){this.series=a;this.applyOptions(b,c);this.pointAttr={};if(a.options.colorByPoint&&(b=a.options.colors||a.chart.options.colors,this.color=this.color||b[a.colorCounter++], +a.colorCounter===b.length))a.colorCounter=0;a.chart.pointCount++;return this},applyOptions:function(a,b){var c=this.series,d=c.pointValKey,a=Ha.prototype.optionsToObject.call(this,a);y(this,a);this.options=this.options?y(this.options,a):a;if(d)this.y=this[d];if(this.x===w&&c)this.x=b===w?c.autoIncrement():b;return this},optionsToObject:function(a){var b,c=this.series,d=c.pointArrayMap||["y"],e=d.length,f=0,g=0;if(typeof a==="number"||a===null)b={y:a};else if(Wa(a)){b={};if(a.length>e){c=typeof a[0]; +if(c==="string")b.name=a[0];else if(c==="number")b.x=a[0];f++}for(;ga+1&&b.push(d.slice(a+1,g)),a=g):g===e-1&&b.push(d.slice(a+1,g+1))});this.segments=b},setOptions:function(a){var b=this.chart.options,c=b.plotOptions, +d=c[this.type];this.userOptions=a;a=z(d,c.series,a);this.tooltipOptions=z(b.tooltip,a.tooltip);d.marker===null&&delete a.marker;return a},getColor:function(){var a=this.options,b=this.userOptions,c=this.chart.options.colors,d=this.chart.counters,e;e=a.color||S[this.type].color;if(!e&&!a.colorByPoint)v(b._colorIndex)?a=b._colorIndex:(b._colorIndex=d.color,a=d.color++),e=c[a];this.color=e;d.wrapColor(c.length)},getSymbol:function(){var a=this.userOptions,b=this.options.marker,c=this.chart,d=c.options.symbols, +c=c.counters;this.symbol=b.symbol;if(!this.symbol)v(a._symbolIndex)?a=a._symbolIndex:(a._symbolIndex=c.symbol,a=c.symbol++),this.symbol=d[a];if(/^url/.test(this.symbol))b.radius=0;c.wrapSymbol(d.length)},drawLegendSymbol:function(a){var b=this.options,c=b.marker,d=a.options.symbolWidth,e=this.chart.renderer,f=this.legendGroup,a=a.baseline,g;if(b.lineWidth){g={"stroke-width":b.lineWidth};if(b.dashStyle)g.dashstyle=b.dashStyle;this.legendLine=e.path(["M",0,a-4,"L",d,a-4]).attr(g).add(f)}if(c&&c.enabled)b= +c.radius,this.legendSymbol=e.symbol(this.symbol,d/2-b,a-4-b,2*b,2*b).add(f)},addPoint:function(a,b,c,d){var e=this.options,f=this.data,g=this.graph,h=this.area,i=this.chart,j=this.xData,k=this.yData,m=this.zData,l=this.names,o=g&&g.shift||0,q=e.data;$a(d,i);if(g&&c)g.shift=o+1;if(h){if(c)h.shift=o+1;h.isArea=!0}b=p(b,!0);d={series:this};this.pointClass.prototype.applyOptions.apply(d,[a]);j.push(d.x);k.push(this.toYData?this.toYData(d):d.y);m.push(d.z);if(l)l[d.x]=d.name;q.push(a);e.legendType==="point"&& +this.generatePoints();c&&(f[0]&&f[0].remove?f[0].remove(!1):(f.shift(),j.shift(),k.shift(),m.shift(),q.shift()));this.getAttribs();this.isDirtyData=this.isDirty=!0;b&&i.redraw()},setData:function(a,b){var c=this.points,d=this.options,e=this.chart,f=null,g=this.xAxis,h=g&&g.categories&&!g.categories.length?[]:null,i;this.xIncrement=null;this.pointRange=g&&g.categories?1:d.pointRange;this.colorCounter=0;var j=[],k=[],m=[],l=a?a.length:[],o=(i=this.pointArrayMap)&&i.length,q=!!this.toYData;if(l>(d.turboThreshold|| +1E3)){for(i=0;f===null&&i1&&j[1]k||this.forceCrop))if(a=i.getExtremes(),i=a.min,k=a.max,b[d-1]k)b=[],c=[];else if(b[0]k){for(a=0;a=i){e=t(0,a-1);break}for(;ak){f=a+1;break}b=b.slice(e,f);c=c.slice(e,f);g=!0}for(a=b.length-1;a>0;a--)if(d=b[a]-b[a-1],d>0&&(h===w||d= +0&&c<=d;)h[c++]=f}this.tooltipPoints=h}},tooltipHeaderFormatter:function(a){var b=this.tooltipOptions,c=b.xDateFormat,d=b.dateTimeLabelFormats,e=this.xAxis,f=e&&e.options.type==="datetime",b=b.headerFormat,e=e&&e.closestPointRange,g;if(f&&!c)if(e)for(g in I){if(I[g]>=e){c=d[g];break}}else c=d.day;f&&c&&Ja(a.key)&&(b=b.replace("{point.key}","{point.key:"+c+"}"));return La(b,{point:a,series:this})},onMouseOver:function(){var a=this.chart,b=a.hoverSeries;if(b&&b!==this)b.onMouseOut();this.options.events.mouseOver&& +K(this,"mouseOver");this.setState("hover");a.hoverSeries=this},onMouseOut:function(){var a=this.options,b=this.chart,c=b.tooltip,d=b.hoverPoint;if(d)d.onMouseOut();this&&a.events.mouseOut&&K(this,"mouseOut");c&&!a.stickyTracking&&(!c.shared||this.noSharedTooltip)&&c.hide();this.setState();b.hoverSeries=null},animate:function(a){var b=this,c=b.chart,d=c.renderer,e;e=b.options.animation;var f=c.clipBox,g=c.inverted,h;if(e&&!da(e))e=S[b.type].animation;h="_sharedClip"+e.duration+e.easing;if(a)a=c[h], +e=c[h+"m"],a||(c[h]=a=d.clipRect(y(f,{width:0})),c[h+"m"]=e=d.clipRect(-99,g?-c.plotLeft:-c.plotTop,99,g?c.chartWidth:c.chartHeight)),b.group.clip(a),b.markerGroup.clip(e),b.sharedClipKey=h;else{if(a=c[h])a.animate({width:c.plotSizeX},e),c[h+"m"].animate({width:c.plotSizeX+99},e);b.animate=null;b.animationTimeout=setTimeout(function(){b.afterAnimate()},e.duration)}},afterAnimate:function(){var a=this.chart,b=this.sharedClipKey,c=this.group;c&&this.options.clip!==!1&&(c.clip(a.clipRect),this.markerGroup.clip()); +setTimeout(function(){b&&a[b]&&(a[b]=a[b].destroy(),a[b+"m"]=a[b+"m"].destroy())},100)},drawPoints:function(){var a,b=this.points,c=this.chart,d,e,f,g,h,i,j,k,m=this.options.marker,l,o=this.markerGroup;if(m.enabled||this._hasPointMarkers)for(f=b.length;f--;)if(g=b[f],d=g.plotX,e=g.plotY,k=g.graphic,i=g.marker||{},a=m.enabled&&i.enabled===w||i.enabled,l=c.isInsidePlot(r(d),e,c.inverted),a&&e!==w&&!isNaN(e)&&g.y!==null)if(a=g.pointAttr[g.selected?"select":""],h=a.r,i=p(i.symbol,this.symbol),j=i.indexOf("url")=== +0,k)k.attr({visibility:l?ca?"inherit":"visible":"hidden"}).animate(y({x:d-h,y:e-h},k.symbolName?{width:2*h,height:2*h}:{}));else{if(l&&(h>0||j))g.graphic=c.renderer.symbol(i,d-h,e-h,2*h,2*h).attr(a).add(o)}else if(k)g.graphic=k.destroy()},convertAttribs:function(a,b,c,d){var e=this.pointAttrToOptions,f,g,h={},a=a||{},b=b||{},c=c||{},d=d||{};for(f in e)g=e[f],h[f]=p(a[g],b[f],c[f],d[f]);return h},getAttribs:function(){var a=this,b=a.options,c=S[a.type].marker?b.marker:b,d=c.states,e=d.hover,f,g=a.color, +h={stroke:g,fill:g},i=a.points||[],j=[],k,m=a.pointAttrToOptions,l=b.negativeColor,o;b.marker?(e.radius=e.radius||c.radius+2,e.lineWidth=e.lineWidth||c.lineWidth+1):e.color=e.color||wa(e.color||g).brighten(e.brightness).get();j[""]=a.convertAttribs(c,h);n(["hover","select"],function(b){j[b]=a.convertAttribs(d[b],j[""])});a.pointAttr=j;for(g=i.length;g--;){h=i[g];if((c=h.options&&h.options.marker||h.options)&&c.enabled===!1)c.radius=0;if(h.negative&&l)h.color=h.fillColor=l;f=b.colorByPoint||h.color; +if(h.options)for(o in m)v(c[m[o]])&&(f=!0);if(f){c=c||{};k=[];d=c.states||{};f=d.hover=d.hover||{};if(!b.marker)f.color=wa(f.color||h.color).brighten(f.brightness||e.brightness).get();k[""]=a.convertAttribs(y({color:h.color},c),j[""]);k.hover=a.convertAttribs(d.hover,j.hover,k[""]);k.select=a.convertAttribs(d.select,j.select,k[""]);if(h.negative&&b.marker&&l)k[""].fill=k.hover.fill=k.select.fill=a.convertAttribs({fillColor:l}).fill}else k=j;h.pointAttr=k}},update:function(a,b){var c=this.chart,d= +this.type,a=z(this.userOptions,{animation:!1,index:this.index,pointStart:this.xData[0]},a);this.remove(!1);y(this,Q[a.type||d].prototype);this.init(c,a);p(b,!0)&&c.redraw(!1)},destroy:function(){var a=this,b=a.chart,c=/AppleWebKit\/533/.test(Ta),d,e,f=a.data||[],g,h,i;K(a,"destroy");X(a);n(["xAxis","yAxis"],function(b){if(i=a[b])na(i.series,a),i.isDirty=i.forceRedraw=!0});a.legendItem&&a.chart.legend.destroyItem(a);for(e=f.length;e--;)(g=f[e])&&g.destroy&&g.destroy();a.points=null;clearTimeout(a.animationTimeout); +n("area,graph,dataLabelsGroup,group,markerGroup,tracker,graphNeg,areaNeg,posClip,negClip".split(","),function(b){a[b]&&(d=c&&b==="group"?"hide":"destroy",a[b][d]())});if(b.hoverSeries===a)b.hoverSeries=null;na(b.series,a);for(h in a)delete a[h]},drawDataLabels:function(){var a=this,b=a.options.dataLabels,c=a.points,d,e,f,g;if(b.enabled||a._hasPointLabels)a.dlProcessOptions&&a.dlProcessOptions(b),g=a.plotGroup("dataLabelsGroup","data-labels",a.visible?"visible":"hidden",b.zIndex||6),e=b,n(c,function(c){var i, +j=c.dataLabel,k,m,l=c.connector,o=!0;d=c.options&&c.options.dataLabels;i=e.enabled||d&&d.enabled;if(j&&!i)c.dataLabel=j.destroy();else if(i){i=b.rotation;b=z(e,d);k=c.getLabelConfig();f=b.format?La(b.format,k):b.formatter.call(k,b);b.style.color=p(b.color,b.style.color,a.color,"black");if(j)if(v(f))j.attr({text:f}),o=!1;else{if(c.dataLabel=j=j.destroy(),l)c.connector=l.destroy()}else if(v(f)){j={fill:b.backgroundColor,stroke:b.borderColor,"stroke-width":b.borderWidth,r:b.borderRadius||0,rotation:i, +padding:b.padding,zIndex:1};for(m in j)j[m]===w&&delete j[m];j=c.dataLabel=a.chart.renderer[i?"text":"label"](f,0,-999,null,null,null,b.useHTML).attr(j).css(b.style).add(g).shadow(b.shadow)}j&&a.alignDataLabel(c,j,b,null,o)}})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=p(a.plotX,-999),a=p(a.plotY,-999),i=b.getBBox(),d=y({x:g?f.plotWidth-a:h,y:r(g?f.plotHeight-h:a),width:0,height:0},d);y(c,{width:i.width,height:i.height});c.rotation?(d={align:c.align,x:d.x+c.x+d.width/2,y:d.y+ +c.y+d.height/2},b[e?"attr":"animate"](d)):b.align(c,null,d);b.attr({visibility:c.crop===!1||f.isInsidePlot(h,a,g)?f.renderer.isSVG?"inherit":"visible":"hidden"})},getSegmentPath:function(a){var b=this,c=[],d=b.options.step;n(a,function(e,f){var g=e.plotX,h=e.plotY,i;b.getPointSpline?c.push.apply(c,b.getPointSpline(a,e,f)):(c.push(f?"L":"M"),d&&f&&(i=a[f-1],d==="right"?c.push(i.plotX,h):d==="center"?c.push((i.plotX+g)/2,i.plotY,(i.plotX+g)/2,h):c.push(g,i.plotY)),c.push(e.plotX,e.plotY))});return c}, +getGraphPath:function(){var a=this,b=[],c,d=[];n(a.segments,function(e){c=a.getSegmentPath(e);e.length>1?b=b.concat(c):d.push(e[0])});a.singlePoints=d;return a.graphPath=b},drawGraph:function(){var a=this,b=this.options,c=[["graph",b.lineColor||this.color]],d=b.lineWidth,e=b.dashStyle,f=this.getGraphPath(),g=b.negativeColor;g&&c.push(["graphNeg",g]);n(c,function(c,g){var j=c[0],k=a[j];if(k)hb(k),k.animate({d:f});else if(d&&f.length){k={stroke:c[1],"stroke-width":d,zIndex:1};if(e)k.dashstyle=e;a[j]= +a.chart.renderer.path(f).attr(k).add(a.group).shadow(!g&&b.shadow)}})},clipNeg:function(){var a=this.options,b=this.chart,c=b.renderer,d=a.negativeColor,e,f=this.graph,g=this.area,h=this.posClip,i=this.negClip;e=b.chartWidth;var j=b.chartHeight,k=t(e,j);if(d&&(f||g))d=pa(this.yAxis.len-this.yAxis.translate(a.threshold||0)),a={x:0,y:0,width:k,height:d},k={x:0,y:d,width:k,height:k-d},b.inverted&&c.isVML&&(a={x:b.plotWidth-d-b.plotLeft,y:0,width:e,height:j},k={x:d+b.plotLeft-e,y:0,width:b.plotLeft+d, +height:e}),this.yAxis.reversed?(b=k,e=a):(b=a,e=k),h?(h.animate(b),i.animate(e)):(this.posClip=h=c.clipRect(b),this.negClip=i=c.clipRect(e),f&&(f.clip(h),this.graphNeg.clip(i)),g&&(g.clip(h),this.areaNeg.clip(i)))},invertGroups:function(){function a(){var a={width:b.yAxis.len,height:b.xAxis.len};n(["group","markerGroup"],function(c){b[c]&&b[c].attr(a).invert()})}var b=this,c=b.chart;if(b.xAxis)F(c,"resize",a),F(b,"destroy",function(){X(c,"resize",a)}),a(),b.invertGroups=a},plotGroup:function(a,b, +c,d,e){var f=this[a],g=!f,h=this.chart,i=this.xAxis,j=this.yAxis;g&&(this[a]=f=h.renderer.g(b).attr({visibility:c,zIndex:d||0.1}).add(e));f[g?"attr":"animate"]({translateX:i?i.left:h.plotLeft,translateY:j?j.top:h.plotTop,scaleX:1,scaleY:1});return f},render:function(){var a=this.chart,b,c=this.options,d=c.animation&&!!this.animate&&a.renderer.isSVG,e=this.visible?"visible":"hidden",f=c.zIndex,g=this.hasRendered,h=a.seriesGroup;b=this.plotGroup("group","series",e,f,h);this.markerGroup=this.plotGroup("markerGroup", +"markers",e,f,h);d&&this.animate(!0);this.getAttribs();b.inverted=this.isCartesian?a.inverted:!1;this.drawGraph&&(this.drawGraph(),this.clipNeg());this.drawDataLabels();this.drawPoints();this.options.enableMouseTracking!==!1&&this.drawTracker();a.inverted&&this.invertGroups();c.clip!==!1&&!this.sharedClipKey&&!g&&b.clip(a.clipRect);d?this.animate():g||this.afterAnimate();this.isDirty=this.isDirtyData=!1;this.hasRendered=!0},redraw:function(){var a=this.chart,b=this.isDirtyData,c=this.group,d=this.xAxis, +e=this.yAxis;c&&(a.inverted&&c.attr({width:a.plotWidth,height:a.plotHeight}),c.animate({translateX:p(d&&d.left,a.plotLeft),translateY:p(e&&e.top,a.plotTop)}));this.translate();this.setTooltipPoints(!0);this.render();b&&K(this,"updatedData")},setState:function(a){var b=this.options,c=this.graph,d=this.graphNeg,e=b.states,b=b.lineWidth,a=a||"";if(this.state!==a)this.state=a,e[a]&&e[a].enabled===!1||(a&&(b=e[a].lineWidth||b+1),c&&!c.dashstyle&&(a={"stroke-width":b},c.attr(a),d&&d.attr(a)))},setVisible:function(a, +b){var c=this,d=c.chart,e=c.legendItem,f,g=d.options.chart.ignoreHiddenSeries,h=c.visible;f=(c.visible=a=c.userOptions.visible=a===w?!h:a)?"show":"hide";n(["group","dataLabelsGroup","markerGroup","tracker"],function(a){if(c[a])c[a][f]()});if(d.hoverSeries===c)c.onMouseOut();e&&d.legend.colorizeItem(c,a);c.isDirty=!0;c.options.stacking&&n(d.series,function(a){if(a.options.stacking&&a.visible)a.isDirty=!0});n(c.linkedSeries,function(b){b.setVisible(a,!1)});if(g)d.isDirtyBox=!0;b!==!1&&d.redraw();K(c, +f)},show:function(){this.setVisible(!0)},hide:function(){this.setVisible(!1)},select:function(a){this.selected=a=a===w?!this.selected:a;if(this.checkbox)this.checkbox.checked=a;K(this,a?"select":"unselect")},drawTracker:function(){var a=this,b=a.options,c=b.trackByArea,d=[].concat(c?a.areaPath:a.graphPath),e=d.length,f=a.chart,g=f.pointer,h=f.renderer,i=f.options.tooltip.snap,j=a.tracker,k=b.cursor,k=k&&{cursor:k},m=a.singlePoints,l,o=function(){if(f.hoverSeries!==a)a.onMouseOver()};if(e&&!c)for(l= +e+1;l--;)d[l]==="M"&&d.splice(l+1,0,d[l+1]-i,d[l+2],"L"),(l&&d[l]==="M"||l===e)&&d.splice(l,0,"L",d[l-2]+i,d[l-1]);for(l=0;l=0;d--)da&&i>e?(i=t(a,e),k=2*e-i):ig&&k>e?(k=t(g,e),i=2*e-k):kh?o-h:g-(f.translate(c.y,0,1,0,1)<=g?h:0));c.barX=q;c.pointWidth=i;c.shapeType="rect";c.shapeArgs=c=b.renderer.Element.prototype.crisp.call(0,e,q,n,j,l);e%2&&(c.y-=1,c.height+=1)})},getSymbol:qa,drawLegendSymbol:D.prototype.drawLegendSymbol,drawGraph:qa,drawPoints:function(){var a=this,b=a.options,c=a.chart.renderer,d;n(a.points,function(e){var f=e.plotY,g=e.graphic;if(f!==w&&!isNaN(f)&&e.y!==null)d=e.shapeArgs,g?(hb(g),g.animate(z(d))):e.graphic= +c[e.shapeType](d).attr(e.pointAttr[e.selected?"select":""]).add(a.group).shadow(b.shadow,null,b.stacking&&!b.borderRadius);else if(g)e.graphic=g.destroy()})},drawTracker:function(){var a=this,b=a.chart.pointer,c=a.options.cursor,d=c&&{cursor:c},e=function(b){var c=b.target,d;for(a.onMouseOver();c&&!d;)d=c.point,c=c.parentNode;if(d!==w)d.onMouseOver(b)};n(a.points,function(a){if(a.graphic)a.graphic.element.point=a;if(a.dataLabel)a.dataLabel.element.point=a});a._hasTracking?a._hasTracking=!0:n(a.trackerGroups, +function(c){if(a[c]&&(a[c].addClass("highcharts-tracker").on("mouseover",e).on("mouseout",function(a){b.onTrackerMouseOut(a)}).css(d),gb))a[c].on("touchstart",e)})},alignDataLabel:function(a,b,c,d,e){var f=this.chart,g=f.inverted,h=a.dlBox||a.shapeArgs,i=a.below||a.plotY>p(this.translatedThreshold,f.plotSizeY),j=p(c.inside,!!this.options.stacking);if(h&&(d=z(h),g&&(d={x:f.plotWidth-d.y-d.height,y:f.plotHeight-d.x-d.width,width:d.height,height:d.width}),!j))g?(d.x+=i?0:d.width,d.width=0):(d.y+=i?d.height: +0,d.height=0);c.align=p(c.align,!g||j?"center":i?"right":"left");c.verticalAlign=p(c.verticalAlign,g||j?"middle":i?"top":"bottom");$.prototype.alignDataLabel.call(this,a,b,c,d,e)},animate:function(a){var b=this.yAxis,c=this.options,d=this.chart.inverted,e={};if(ca)a?(e.scaleY=0.001,a=A(b.pos+b.len,t(b.pos,b.toPixels(c.threshold))),d?e.translateX=a-b.len:e.translateY=a,this.group.attr(e)):(e.scaleY=1,e[d?"translateX":"translateY"]=b.pos,this.group.animate(e,this.options.animation),this.animate=null)}, +remove:function(){var a=this,b=a.chart;b.hasRendered&&n(b.series,function(b){if(b.type===a.type)b.isDirty=!0});$.prototype.remove.apply(a,arguments)}});Q.column=Z;S.bar=z(S.column);Va=ea(Z,{type:"bar",inverted:!0});Q.bar=Va;S.scatter=z(R,{lineWidth:0,tooltip:{headerFormat:'{series.name}
    ',pointFormat:"x: {point.x}
    y: {point.y}
    ",followPointer:!0},stickyTracking:!1});Va=ea($,{type:"scatter",sorted:!1,requireSorting:!1, +noSharedTooltip:!0,trackerGroups:["markerGroup"],drawTracker:Z.prototype.drawTracker,setTooltipPoints:qa});Q.scatter=Va;S.pie=z(R,{borderColor:"#FFFFFF",borderWidth:1,center:[null,null],clip:!1,colorByPoint:!0,dataLabels:{distance:30,enabled:!0,formatter:function(){return this.point.name}},ignoreHiddenPoint:!0,legendType:"point",marker:null,size:null,showInLegend:!1,slicedOffset:10,states:{hover:{brightness:0.1,shadow:!1}},stickyTracking:!1,tooltip:{followPointer:!0}});R={type:"pie",isCartesian:!1, +pointClass:ea(Ha,{init:function(){Ha.prototype.init.apply(this,arguments);var a=this,b;if(a.y<0)a.y=null;y(a,{visible:a.visible!==!1,name:p(a.name,"Slice")});b=function(){a.slice()};F(a,"select",b);F(a,"unselect",b);return a},setVisible:function(a){var b=this,c=b.series,d=c.chart,e;b.visible=b.options.visible=a=a===w?!b.visible:a;c.options.data[va(b,c.data)]=b.options;e=a?"show":"hide";n(["graphic","dataLabel","connector","shadowGroup"],function(a){if(b[a])b[a][e]()});b.legendItem&&d.legend.colorizeItem(b, +a);if(!c.isDirty&&c.options.ignoreHiddenPoint)c.isDirty=!0,d.redraw()},slice:function(a,b,c){var d=this.series;$a(c,d.chart);p(b,!0);this.sliced=this.options.sliced=a=v(a)?a:!this.sliced;d.options.data[va(this,d.data)]=this.options;a=a?this.slicedTranslation:{translateX:0,translateY:0};this.graphic.animate(a);this.shadowGroup&&this.shadowGroup.animate(a)}}),requireSorting:!1,noSharedTooltip:!0,trackerGroups:["group","dataLabelsGroup"],pointAttrToOptions:{stroke:"borderColor","stroke-width":"borderWidth", +fill:"color"},getColor:qa,animate:function(a){var b=this,c=b.points,d=b.startAngleRad;if(!a)n(c,function(a){var c=a.graphic,a=a.shapeArgs;c&&(c.attr({r:b.center[3]/2,start:d,end:d}),c.animate({r:a.r,start:a.start,end:a.end},b.options.animation))}),b.animate=null},setData:function(a,b){$.prototype.setData.call(this,a,!1);this.processData();this.generatePoints();p(b,!0)&&this.chart.redraw()},getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight- +2*c,b=a.center,a=[p(b[0],"50%"),p(b[1],"50%"),a.size||"100%",a.innerSize||0],g=A(e,f),h;return Fa(a,function(a,b){h=/%$/.test(a);d=b<2||b===2&&h;return(h?[e,f,g,g][b]*C(a)/100:a)+(d?c:0)})},translate:function(a){this.generatePoints();var b=0,c=0,d=this.options,e=d.slicedOffset,f=e+d.borderWidth,g,h,i,j=this.startAngleRad=bb/180*((d.startAngle||0)%360-90),k=this.points,m=2*bb,l=d.dataLabels.distance,o=d.ignoreHiddenPoint,q,n=k.length,p;if(!a)this.center=a=this.getCenter();this.getX=function(b,c){i= +P.asin((b-a[1])/(a[2]/2+l));return a[0]+(c?-1:1)*ha(i)*(a[2]/2+l)};for(q=0;q0.75*m&&(i-=2*bb);p.slicedTranslation={translateX:r(ha(i)*e),translateY:r(ka(i)*e)};g=ha(i)*a[2]/2;h=ka(i)*a[2]/2;p.tooltipPos=[a[0]+g*0.7,a[1]+h*0.7];p.half=i0,u,s,x,w,z=[[],[]],E,B,y,A,J,C=[0,0,0,0], +db=function(a,b){return b.y-a.y},F=function(a,b){a.sort(function(a,c){return a.angle!==void 0&&(c.angle-a.angle)*b})};if(e.enabled||a._hasPointLabels){$.prototype.drawDataLabels.apply(a);n(b,function(a){a.dataLabel&&z[a.half].push(a)});for(A=0;!w&&b[A];)w=b[A]&&b[A].dataLabel&&(b[A].dataLabel.getBBox().height||21),A++;for(A=2;A--;){var b=[],G=[],I=z[A],H=I.length,D;F(I,A-0.5);if(m>0){for(J=q-o-m;J<=q+o+m;J+=w)b.push(J);s=b.length;if(H>s){c=[].concat(I);c.sort(db);for(J=H;J--;)c[J].rank=J;for(J=H;J--;)I[J].rank>= +s&&I.splice(J,1);H=I.length}for(J=0;J0){if(s=G.pop(),D=s.i,B=s.y,c>B&&b[D+1]!==null||ch-f&&(C[1]=t(r(E+s-h+f),C[1])),B-w/2<0?C[0]=t(r(-B+w/2),C[0]):B+w/2>d&&(C[2]=t(r(B+w/2-d),C[2]))}}if(ua(C)===0||this.verifyDataLabelOverflow(C))this.placeDataLabels(),v&&g&&n(this.points,function(b){i=b.connector;x=b.labelPos;if((u=b.dataLabel)&&u._pos)y=u._attr.visibility,E=u.connX,B=u.connY,j=k?["M",E+(x[6]==="left"?5:-5),B, +"C",E,B,2*x[2]-x[4],2*x[3]-x[5],x[2],x[3],"L",x[4],x[5]]:["M",E+(x[6]==="left"?5:-5),B,"L",x[2],x[3],"L",x[4],x[5]],i?(i.animate({d:j}),i.attr("visibility",y)):b.connector=i=a.chart.renderer.path(j).attr({"stroke-width":g,stroke:e.connectorColor||b.color||"#606060",visibility:y}).add(a.group);else if(i)b.connector=i.destroy()})}},verifyDataLabelOverflow:function(a){var b=this.center,c=this.options,d=c.center,e=c=c.minSize||80,f;d[0]!==null?e=t(b[2]-t(a[1],a[3]),c):(e=t(b[2]-a[1]-a[3],c),b[0]+=(a[3]- +a[1])/2);d[1]!==null?e=t(A(e,b[2]-t(a[0],a[2])),c):(e=t(A(e,b[2]-a[0]-a[2]),c),b[1]+=(a[0]-a[2])/2);e=c[1]||n===i;)if(j=c.shift(), +k=d.apply(0,l),k!==w&&(g.push(j),h.push(k)),l[0]=[],l[1]=[],l[2]=[],l[3]=[],n===i)break;if(n===i)break;if(o){j=this.cropStart+n;j=e&&e[j]||this.pointClass.prototype.applyOptions.apply({series:this},[f[j]]);var p;for(k=0;kg/i||j&&c.forced){e=!0;this.points=null;a=h.getExtremes();j=a.min;k=a.max;a=h.getGroupIntervalFactor&& +h.getGroupIntervalFactor(j,k,d)||1;g=i*(k-j)/g*a;h=(h.getNonLinearTimeTicks||fb)(zb(g,c.units||bc),j,k,null,d,this.closestPointRange);f=V.groupData.apply(this,[d,f,h,c.approximation]);d=f[0];f=f[1];if(c.smoothed){a=d.length-1;for(d[a]=k;a--&&a>0;)d[a]+=g/2;d[0]=j}this.currentDataGrouping=h.info;if(b.pointRange===null)this.pointRange=h.info.totalRange;this.closestPointRange=h.info.totalRange;this.processedXData=d;this.processedYData=f}else this.currentDataGrouping=null,this.pointRange=m;this.hasGroupedData= +e}};V.destroyGroupedData=function(){var a=this.groupedData;n(a||[],function(b,c){b&&(a[c]=b.destroy?b.destroy():null)});this.groupedData=null};V.generatePoints=function(){gc.apply(this);this.destroyGroupedData();this.groupedData=this.hasGroupedData?this.points:null};V.tooltipHeaderFormatter=function(a){var b=this.tooltipOptions,c=this.options.dataGrouping,d=b.xDateFormat,e,f=this.xAxis,g,h;if(f&&f.options.type==="datetime"&&c&&Ja(a.key)){g=this.currentDataGrouping;c=c.dateTimeLabelFormats;if(g)f= +c[g.unitName],g.count===1?d=f[0]:(d=f[1],e=f[2]);else if(!d&&c)for(h in I)if(I[h]>=f.closestPointRange){d=c[h][0];break}d=ya(d,a.key);e&&(d+=ya(e,a.key+g.totalRange-1));a=b.headerFormat.replace("{point.key}",d)}else a=ic.call(this,a);return a};V.destroy=function(){for(var a=this.groupedData||[],b=a.length;b--;)a[b]&&a[b].destroy();hc.apply(this)};sa(V,"setOptions",function(a,b){var c=a.call(this,b),d=this.type,e=this.chart.options.plotOptions,f=S[d].dataGrouping;if(ac[d])f||(f=z(jc,ac[d])),c.dataGrouping= +z(f,e.series&&e.series.dataGrouping,e[d].dataGrouping,b.dataGrouping);if(this.chart.options._stock)this.requireSorting=!0;return c});S.ohlc=z(S.column,{lineWidth:1,tooltip:{pointFormat:'{series.name}
    Open: {point.open}
    High: {point.high}
    Low: {point.low}
    Close: {point.close}
    '},states:{hover:{lineWidth:3}},threshold:null});R=ea(Q.column,{type:"ohlc",pointArrayMap:["open","high","low","close"],toYData:function(a){return[a.open, +a.high,a.low,a.close]},pointValKey:"high",pointAttrToOptions:{stroke:"color","stroke-width":"lineWidth"},upColorProp:"stroke",getAttribs:function(){Q.column.prototype.getAttribs.apply(this,arguments);var a=this.options,b=a.states,a=a.upColor||this.color,c=z(this.pointAttr),d=this.upColorProp;c[""][d]=a;c.hover[d]=b.hover.upColor||a;c.select[d]=b.select.upColor||a;n(this.points,function(a){if(a.open"},threshold:null,y:-30});Q.flags=ea(Q.column,{type:"flags",sorted:!1,noSharedTooltip:!0,takeOrdinalPosition:!1,forceCrop:!0,init:$.prototype.init,pointAttrToOptions:{fill:"fillColor",stroke:"color","stroke-width":"lineWidth",r:"radius"},translate:function(){Q.column.prototype.translate.apply(this);var a=this.chart,b=this.points,c=b.length-1,d,e,f=this.options.onSeries,f=(d=f&& +a.get(f))&&d.options.step,g=d&&d.points,h=g&&g.length,i=this.xAxis,j=i.getExtremes(),k,m,l;if(d&&d.visible&&h){m=g[h-1].x;for(b.sort(function(a,b){return a.x-b.x});h--&&b[c];)if(d=b[c],k=g[h],k.x<=d.x&&k.plotY!==w){if(d.x<=m)d.plotY=k.plotY,k.x=j.min&&c.x<=j.max?c.plotY=i.lineTop-a.plotTop:c.shapeArgs={};if((e=b[d-1])&&e.plotX===c.plotX){if(e.stackIndex=== +w)e.stackIndex=0;c.stackIndex=e.stackIndex+1}})},drawPoints:function(){var a,b=this.points,c=this.chart.renderer,d,e,f=this.options,g=f.y,h,i,j,k,m,l=f.lineWidth%2/2,o;for(j=b.length;j--;)if(k=b[j],d=k.plotX+l,a=k.stackIndex,h=k.options.shape||f.shape,e=k.plotY,e!==w&&(e=k.plotY+g+l-(a!==w&&a*f.stackDistance)),i=a?w:k.plotX+l,o=a?w:k.plotY,m=k.graphic,e!==w)a=k.pointAttr[k.selected?"select":""],m?m.attr({x:d,y:e,r:a.r,anchorX:i,anchorY:o}):m=k.graphic=c.label(k.options.title||f.title||"A",d,e,h,i, +o,f.useHTML).css(z(f.style,k.style)).attr(a).attr({align:h==="flag"?"left":"center",width:f.width,height:f.height}).add(this.group).shadow(f.shadow),i=m.box,a=i.getBBox(),k.shapeArgs=y(a,{x:d-(h==="flag"?0:i.attr("width")/2),y:e});else if(m)k.graphic=m.destroy()},drawTracker:function(){Q.column.prototype.drawTracker.apply(this);ca&&n(this.points,function(a){a.graphic&&F(a.graphic.element,"mouseover",function(){a.graphic.toFront()})})},animate:qa});xb.flag=function(a,b,c,d,e){var f=e&&e.anchorX||a, +e=e&&e.anchorY||b;return["M",f,e,"L",a,b+d,a,b,a+c,b,a+c,b+d,a,b+d,"M",f,e,"Z"]};n(["circle","square"],function(a){xb[a+"pin"]=function(b,c,d,e,f){var g=f&&f.anchorX,f=f&&f.anchorY,b=xb[a](b,c,d,e);g&&f&&b.push("M",g,c>f?c:c+e,"L",g,f);return b}});cb===ib&&n(["flag","circlepin","squarepin"],function(a){ib.prototype.symbols[a]=xb[a]});R=jb("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#FFF"],[1,"#CCC"]]);D=[].concat(bc);D[4]=[ga,[1,2,3,4]];D[5]=[Ma,[1,2,3]];y(M,{navigator:{handles:{backgroundColor:"#FFF", +borderColor:"#666"},height:40,margin:10,maskFill:"rgba(255, 255, 255, 0.75)",outlineColor:"#444",outlineWidth:1,series:{type:"areaspline",color:"#4572A7",compare:null,fillOpacity:0.4,dataGrouping:{approximation:"average",groupPixelWidth:2,smoothed:!0,units:D},dataLabels:{enabled:!1,zIndex:2},id:"highcharts-navigator-series",lineColor:"#4572A7",lineWidth:1,marker:{enabled:!1},pointRange:0,shadow:!1},xAxis:{tickWidth:0,lineWidth:0,gridLineWidth:1,tickPixelInterval:200,labels:{align:"left",x:3,y:-4}}, +yAxis:{gridLineWidth:0,startOnTick:!1,endOnTick:!1,minPadding:0.1,maxPadding:0.1,labels:{enabled:!1},title:{text:null},tickWidth:0}},scrollbar:{height:ub?20:14,barBackgroundColor:R,barBorderRadius:2,barBorderWidth:1,barBorderColor:"#666",buttonArrowColor:"#666",buttonBackgroundColor:R,buttonBorderColor:"#666",buttonBorderRadius:2,buttonBorderWidth:1,minWidth:6,rifleColor:"#666",trackBackgroundColor:jb("linearGradient",{x1:0,y1:0,x2:0,y2:1},"stops",[[0,"#EEE"],[1,"#FFF"]]),trackBorderColor:"#CCC", +trackBorderWidth:1,liveRedraw:ca}});Ib.prototype={drawHandle:function(a,b){var c=this.chart,d=c.renderer,e=this.elementsToDestroy,f=this.handles,g=this.navigatorOptions.handles,g={fill:g.backgroundColor,stroke:g.borderColor,"stroke-width":1},h;this.rendered||(f[b]=d.g().css({cursor:"e-resize"}).attr({zIndex:4-b}).add(),h=d.rect(-4.5,0,9,16,3,1).attr(g).add(f[b]),e.push(h),h=d.path(["M",-1.5,4,"L",-1.5,12,"M",0.5,4,"L",0.5,12]).attr(g).add(f[b]),e.push(h));f[b][c.isResizing?"animate":"attr"]({translateX:this.scrollerLeft+ +this.scrollbarHeight+parseInt(a,10),translateY:this.top+this.height/2-8})},drawScrollbarButton:function(a){var b=this.chart.renderer,c=this.elementsToDestroy,d=this.scrollbarButtons,e=this.scrollbarHeight,f=this.scrollbarOptions,g;this.rendered||(d[a]=b.g().add(this.scrollbarGroup),g=b.rect(-0.5,-0.5,e+1,e+1,f.buttonBorderRadius,f.buttonBorderWidth).attr({stroke:f.buttonBorderColor,"stroke-width":f.buttonBorderWidth,fill:f.buttonBackgroundColor}).add(d[a]),c.push(g),g=b.path(["M",e/2+(a?-1:1),e/2- +3,"L",e/2+(a?-1:1),e/2+3,e/2+(a?2:-2),e/2]).attr({fill:f.buttonArrowColor}).add(d[a]),c.push(g));a&&d[a].attr({translateX:this.scrollerWidth-e})},render:function(a,b,c,d){var e=this.chart,f=e.renderer,g,h,i,j,k=this.scrollbarGroup,m=this.navigatorGroup,l=this.scrollbar,m=this.xAxis,o=this.scrollbarTrack,n=this.scrollbarHeight,v=this.scrollbarEnabled,u=this.navigatorOptions,s=this.scrollbarOptions,x=s.minWidth,w=this.height,z=this.top,E=this.navigatorEnabled,B=u.outlineWidth,y=B/2,D=0,J=this.outlineHeight, +I=s.barBorderRadius,H=s.barBorderWidth,F=z+y;if(!isNaN(a)){this.navigatorLeft=g=p(m.left,e.plotLeft+n);this.navigatorWidth=h=p(m.len,e.plotWidth-2*n);this.scrollerLeft=i=g-n;this.scrollerWidth=j=j=h+2*n;if(m.getExtremes){var G=e.xAxis[0].getExtremes(),L=G.dataMin===null,K=m.getExtremes(),M=A(G.dataMin,K.dataMin),G=t(G.dataMax,K.dataMax);!L&&(M!==K.min||G!==K.max)&&m.setExtremes(M,G,!0,!1)}c=p(c,m.translate(a));d=p(d,m.translate(b));this.zoomedMax=a=A(C(t(c,d)),h);this.zoomedMin=d=this.fixedWidth? +a-this.fixedWidth:t(C(A(c,d)),0);this.range=c=a-d;if(!this.rendered){if(E)this.navigatorGroup=m=f.g("navigator").attr({zIndex:3}).add(),this.leftShade=f.rect().attr({fill:u.maskFill}).add(m),this.rightShade=f.rect().attr({fill:u.maskFill}).add(m),this.outline=f.path().attr({"stroke-width":B,stroke:u.outlineColor}).add(m);if(v)this.scrollbarGroup=k=f.g("scrollbar").add(),l=s.trackBorderWidth,this.scrollbarTrack=o=f.rect().attr({y:-l%2/2,fill:s.trackBackgroundColor,stroke:s.trackBorderColor,"stroke-width":l, +r:s.trackBorderRadius||0,height:n}).add(k),this.scrollbar=l=f.rect().attr({y:-H%2/2,height:n,fill:s.barBackgroundColor,stroke:s.barBorderColor,"stroke-width":H,r:I}).add(k),this.scrollbarRifles=f.path().attr({stroke:s.rifleColor,"stroke-width":1}).add(k)}e=e.isResizing?"animate":"attr";E&&(this.leftShade[e]({x:g,y:z,width:d,height:w}),this.rightShade[e]({x:g+a,y:z,width:h-a,height:w}),this.outline[e]({d:["M",i,F,"L",g+d+y,F,g+d+y,F+J-n,"M",g+a-y,F+J-n,"L",g+a-y,F,i+j,F]}),this.drawHandle(d+y,0),this.drawHandle(a+ +y,1));if(v)this.drawScrollbarButton(0),this.drawScrollbarButton(1),k[e]({translateX:i,translateY:r(F+w)}),o[e]({width:j}),g=n+d,h=c-H,h12?"visible":"hidden"})[e]({d:["M",x-3,n/4,"L",x-3,2*n/3,"M",x,n/4,"L",x,2*n/3,"M",x+3,n/4,"L",x+3,2*n/3]});this.scrollbarPad=D;this.rendered=!0}},addEvents:function(){var a=this.chart.container,b=this.mouseDownHandler,c=this.mouseMoveHandler, +d=this.mouseUpHandler,e;e=[[a,"mousedown",b],[a,"mousemove",c],[document,"mouseup",d]];gb&&e.push([a,"touchstart",b],[a,"touchmove",c],[document,"touchend",d]);n(e,function(a){F.apply(null,a)});this._events=e},removeEvents:function(){n(this._events,function(a){X.apply(null,a)});this._events=w;this.navigatorEnabled&&this.baseSeries&&X(this.baseSeries,"updatedData",this.updatedDataHandler)},init:function(){var a=this,b=a.chart,c,d,e=a.scrollbarHeight,f=a.navigatorOptions,g=a.height,h=a.top,i,j,k,m= +document.body.style,l,o=a.baseSeries,n;a.mouseDownHandler=function(d){var d=b.pointer.normalize(d),e=a.zoomedMin,f=a.zoomedMax,h=a.top,i=a.scrollbarHeight,k=a.scrollerLeft,o=a.scrollerWidth,n=a.navigatorLeft,p=a.navigatorWidth,q=a.scrollbarPad,r=a.range,s=d.chartX,u=d.chartY,d=b.xAxis[0],t=ub?10:7;if(u>h&&un+e-q&&sk&&sk+o-i?e+A(10,r):sp&&(f=p-r),f!==e)){a.fixedWidth=r;if(!d.ordinalPositions)d.fixedRange=d.max-d.min;e=c.translate(f,!0);d.setExtremes(e,d.fixedRange?e+d.fixedRange:c.translate(f+r,!0),!0,!1,{trigger:"navigator"})}};a.mouseMoveHandler=function(c){var d=a.scrollbarHeight,e=a.navigatorLeft,f=a.navigatorWidth,g=a.scrollerLeft,h=a.scrollerWidth,i=a.range,l;if(c.pageX!== +0)c=b.pointer.normalize(c),l=c.chartX,lg+h-d&&(l=g+h-d),a.grabbedLeft?(k=!0,a.render(0,0,l-e,a.otherHandlePos)):a.grabbedRight?(k=!0,a.render(0,0,a.otherHandlePos,l-e)):a.grabbedCenter&&(k=!0,lf+j-i&&(l=f+j-i),a.render(0,0,l-j,l-j+i)),k&&a.scrollbarOptions.liveRedraw&&setTimeout(function(){a.mouseUpHandler(c)},0)};a.mouseUpHandler=function(d){k&&b.xAxis[0].setExtremes(c.translate(a.zoomedMin,!0),c.translate(a.zoomedMax,!0),!0,!1,{trigger:"navigator",DOMEvent:d});if(d.type!=="mousemove")a.grabbedLeft= +a.grabbedRight=a.grabbedCenter=a.fixedWidth=k=j=null,m.cursor=l||""};a.updatedDataHandler=function(){var c=o.xAxis,d=c.getExtremes(),e=d.min,f=d.max,g=d.dataMin,d=d.dataMax,h=f-e,j,k,l,m,p;j=i.xData;var r=!!c.setExtremes;k=f>=j[j.length-1];j=e<=g;if(!n)i.options.pointStart=o.xData[0],i.setData(o.options.data,!1),p=!0;j&&(m=g,l=m+h);k&&(l=d,j||(m=t(l-h,i.xData[0])));r&&(j||k)?c.setExtremes(m,l,!0,!1,{trigger:"updatedData"}):(p&&b.redraw(!1),a.render(t(e,g),A(f,d)))};var r=b.xAxis.length,u=b.yAxis.length; +b.extraBottomMargin=a.outlineHeight+f.margin;if(a.navigatorEnabled){var s=o?o.options:{},x=s.data,v=f.series;n=v.data;a.xAxis=c=new Da(b,z({ordinal:o&&o.xAxis.options.ordinal},f.xAxis,{isX:!0,type:"datetime",index:r,height:g,offset:0,offsetLeft:e,offsetRight:-e,startOnTick:!1,endOnTick:!1,minPadding:0,maxPadding:0,zoomEnabled:!1}));a.yAxis=d=new Da(b,z(f.yAxis,{alignTicks:!1,height:g,offset:0,index:u,zoomEnabled:!1}));r=z(s,v,{threshold:null,clip:!1,enableMouseTracking:!1,group:"nav",padXAxis:!1, +xAxis:r,yAxis:u,name:"Navigator",showInLegend:!1,isInternal:!0,visible:!0});r.data=n||x;i=b.initSeries(r);if(o&&f.adaptToUpdatedData!==!1)F(o,"updatedData",a.updatedDataHandler),o.userOptions.events=y(o.userOptions.event,{updatedData:a.updatedDataHandler})}else a.xAxis=c={translate:function(a,c){var d=b.xAxis[0].getExtremes(),f=b.plotWidth-2*e,g=d.dataMin,d=d.dataMax-g;return c?a*d/f+g:f*(a-g)/d}};a.series=i;sa(b,"getMargins",function(b){var e=this.legend,f=e.options;b.call(this);a.top=h=a.navigatorOptions.top|| +this.chartHeight-a.height-a.scrollbarHeight-this.options.chart.spacingBottom-(f.verticalAlign==="bottom"&&f.enabled&&!f.floating?e.legendHeight+p(f.margin,10):0);if(c&&d)c.options.top=d.options.top=h,c.setAxisSize(),d.setAxisSize()});a.addEvents()},destroy:function(){this.removeEvents();n([this.xAxis,this.yAxis,this.leftShade,this.rightShade,this.outline,this.scrollbarTrack,this.scrollbarRifles,this.scrollbarGroup,this.scrollbar],function(a){a&&a.destroy&&a.destroy()});this.xAxis=this.yAxis=this.leftShade= +this.rightShade=this.outline=this.scrollbarTrack=this.scrollbarRifles=this.scrollbarGroup=this.scrollbar=null;n([this.scrollbarButtons,this.handles,this.elementsToDestroy],function(a){Aa(a)})}};Highcharts.Scroller=Ib;sa(Da.prototype,"zoom",function(a,b,c){var d=this.chart,e=d.options,f=e.chart.zoomType,g=e.navigator,e=e.rangeSelector,h;if(this.isXAxis&&(g&&g.enabled||e&&e.enabled))if(f==="x")d.resetZoomButton="blocked";else if(f==="y")h=!1;else if(f==="xy")d=this.previousZoom,v(b)?this.previousZoom= +[this.min,this.max]:d&&(b=d[0],c=d[1],delete this.previousZoom);return h!==w?h:a.call(this,b,c)});sa(Sa.prototype,"init",function(a,b,c){F(this,"beforeRender",function(){var a=this.options;if(a.navigator.enabled||a.scrollbar.enabled)this.scroller=new Ib(this)});a.call(this,b,c)});y(M,{rangeSelector:{buttonTheme:{width:28,height:16,padding:1,r:0,stroke:"#68A",zIndex:7},inputPosition:{align:"right"},labelStyle:{color:"#666"}}});M.lang=z(M.lang,{rangeSelectorZoom:"Zoom",rangeSelectorFrom:"From",rangeSelectorTo:"To"}); +Jb.prototype={clickButton:function(a,b,c){var d=this,e=d.chart,f=d.buttons,g=e.xAxis[0],h=g&&g.getExtremes(),i=e.scroller&&e.scroller.xAxis,j=i&&i.getExtremes&&i.getExtremes(),i=j&&j.dataMin,j=j&&j.dataMax,k=h&&h.dataMin,m=h&&h.dataMax,l=(v(k)&&v(i)?A:p)(k,i),o=(v(m)&&v(j)?t:p)(m,j),q,h=g&&A(h.max,p(o,h.max)),i=new Date(h),j=b.type,k=b.count,r,u,m={millisecond:1,second:1E3,minute:6E4,hour:36E5,day:864E5,week:6048E5};if(!(l===null||o===null||a===d.selected)){if(m[j])r=m[j]*k,q=t(h-r,l);else if(j=== +"month"||j==="year")r={month:"Month",year:"FullYear"}[j],i["set"+r](i["get"+r]()-k),q=t(i.getTime(),p(l,Number.MIN_VALUE)),r={month:30,year:365}[j]*864E5*k;else if(j==="ytd")if(g){if(o===w)l=Number.MAX_VALUE,o=Number.MIN_VALUE,n(e.series,function(a){a=a.xData;l=A(a[0],l);o=t(a[a.length-1],o)}),c=!1;h=new Date(o);u=h.getFullYear();q=u=t(l||0,Date.UTC(u,0,1));h=h.getTime();h=A(o||h,h)}else{F(e,"beforeRender",function(){d.clickButton(a,b)});return}else j==="all"&&g&&(q=l,h=o);f[a]&&f[a].setState(2); +g?g.setExtremes(q,h,p(c,1),0,{trigger:"rangeSelectorButton",rangeSelectorButton:b}):(c=e.options.xAxis,c[0]=z(c[0],{range:r,min:u}));d.selected=a}},defaultButtons:[{type:"month",count:1,text:"1m"},{type:"month",count:3,text:"3m"},{type:"month",count:6,text:"6m"},{type:"ytd",text:"YTD"},{type:"year",count:1,text:"1y"},{type:"all",text:"All"}],init:function(a){var b=this,c=a.options.rangeSelector,d=c.buttons||[].concat(b.defaultButtons),e=b.buttons=[],c=c.selected,f=b.blurInputs=function(){var a=b.minInput, +c=b.maxInput;a&&a.blur();c&&c.blur()};b.chart=a;a.extraTopMargin=25;b.buttonOptions=d;F(a.container,"mousedown",f);F(a,"resize",f);c!==w&&d[c]&&this.clickButton(c,d[c],!1);F(a,"load",function(){F(a.xAxis[0],"afterSetExtremes",function(){if(this.fixedRange!==this.max-this.min)e[b.selected]&&!a.renderer.forExport&&e[b.selected].setState(0),b.selected=null;this.fixedRange=null})})},setInputValue:function(a,b){var c=this.chart.options.rangeSelector;if(b)this[a+"Input"].HCTime=b;this[a+"Input"].value= +ya(c.inputEditDateFormat||"%Y-%m-%d",this[a+"Input"].HCTime);this[a+"DateBox"].attr({text:ya(c.inputDateFormat||"%b %e, %Y",this[a+"Input"].HCTime)})},drawInput:function(a){var b=this,c=b.chart,d=c.options.chart.style,e=c.renderer,f=c.options.rangeSelector,g=b.div,h=a==="min",i,j,k,m=this.inputGroup;this[a+"Label"]=j=e.label(M.lang[h?"rangeSelectorFrom":"rangeSelectorTo"],this.inputGroup.offset).attr({padding:1}).css(z(d,f.labelStyle)).add(m);m.offset+=j.width+5;this[a+"DateBox"]=k=e.label("",m.offset).attr({padding:1, +width:90,height:16,stroke:"silver","stroke-width":1}).css(z({textAlign:"center"},d,f.inputStyle)).on("click",function(){b[a+"Input"].focus()}).add(m);m.offset+=k.width+(h?10:0);this[a+"Input"]=i=aa("input",{name:a,className:"highcharts-range-selector",type:"text"},y({position:"absolute",border:0,width:"1px",height:"1px",padding:0,textAlign:"center",fontSize:d.fontSize,fontFamily:d.fontFamily,top:c.plotTop+"px"},f.inputStyle),g);i.onfocus=function(){L(this,{left:m.translateX+k.x+"px",top:m.translateY+ +"px",width:k.width-2+"px",height:k.height-2+"px",border:"2px solid silver"})};i.onblur=function(){L(this,{border:0,width:"1px",height:"1px"});b.setInputValue(a)};i.onchange=function(){var a=i.value,d=Date.parse(a),e=c.xAxis[0].getExtremes();isNaN(d)&&(d=a.split("-"),d=Date.UTC(C(d[0]),C(d[1])-1,C(d[2])));if(!isNaN(d)&&(M.global.useUTC||(d+=(new Date).getTimezoneOffset()*6E4),h&&d>=e.dataMin&&d<=b.maxInput.HCTime||!h&&d<=e.dataMax&&d>=b.minInput.HCTime))c.xAxis[0].setExtremes(h?d:e.min,h?e.max:d,w, +w,{trigger:"rangeSelectorInput"})}},render:function(a,b){var c=this,d=c.chart,e=d.renderer,f=d.container,g=d.options,h=g.exporting&&d.options.navigation.buttonOptions,i=g.rangeSelector,j=c.buttons,k=M.lang,m=c.div,m=c.inputGroup,l=i.buttonTheme,o=i.inputEnabled!==!1,p=l&&l.states,r=d.plotLeft,u;if(!c.rendered&&(c.zoomText=e.text(k.rangeSelectorZoom,r,d.plotTop-10).css(i.labelStyle).add(),u=r+c.zoomText.getBBox().width+5,n(c.buttonOptions,function(a,b){j[b]=e.button(a.text,u,d.plotTop-25,function(){c.clickButton(b, +a);c.isActive=!0},l,p&&p.hover,p&&p.select).css({textAlign:"center"}).add();u+=j[b].width+(i.buttonSpacing||0);c.selected===b&&j[b].setState(2)}),o))c.div=m=aa("div",null,{position:"relative",height:0,zIndex:1}),f.parentNode.insertBefore(m,f),c.inputGroup=m=e.g("input-group").add(),m.offset=0,c.drawInput("min"),c.drawInput("max");o&&(f=d.plotTop-35,m.align(y({y:f,width:m.offset,x:h&&f<(h.y||0)+h.height-g.chart.spacingTop?-40:0},i.inputPosition),!0,d.spacingBox),c.setInputValue("min",a),c.setInputValue("max", +b));c.rendered=!0},destroy:function(){var a=this.minInput,b=this.maxInput,c=this.chart,d=this.blurInputs,e;X(c.container,"mousedown",d);X(c,"resize",d);Aa(this.buttons);if(a)a.onfocus=a.onblur=a.onchange=null;if(b)b.onfocus=b.onblur=b.onchange=null;for(e in this)this[e]&&e!=="chart"&&(this[e].destroy?this[e].destroy():this[e].nodeType&&Za(this[e])),this[e]=null}};sa(Sa.prototype,"init",function(a,b,c){F(this,"init",function(){if(this.options.rangeSelector.enabled)this.rangeSelector=new Jb(this)}); +a.call(this,b,c)});Highcharts.RangeSelector=Jb;Sa.prototype.callbacks.push(function(a){function b(){f=a.xAxis[0].getExtremes();g.render(t(f.min,f.dataMin),A(f.max,p(f.dataMax,Number.MAX_VALUE)))}function c(){f=a.xAxis[0].getExtremes();h.render(f.min,f.max)}function d(a){g.render(a.min,a.max)}function e(a){h.render(a.min,a.max)}var f,g=a.scroller,h=a.rangeSelector;g&&(F(a.xAxis[0],"afterSetExtremes",d),sa(a,"drawChartBox",function(a){var c=this.isDirtyBox;a.call(this);c&&b()}),b());h&&(F(a.xAxis[0], +"afterSetExtremes",e),F(a,"resize",c),c());F(a,"destroy",function(){g&&X(a.xAxis[0],"afterSetExtremes",d);h&&(X(a,"resize",c),X(a.xAxis[0],"afterSetExtremes",e))})});Highcharts.StockChart=function(a,b){var c=a.series,d,e={marker:{enabled:!1,states:{hover:{radius:5}}},states:{hover:{lineWidth:2}}},f={shadow:!1,borderWidth:0};a.xAxis=Fa(ja(a.xAxis||{}),function(a){return z({minPadding:0,maxPadding:0,ordinal:!0,title:{text:null},labels:{overflow:"justify"},showLastLabel:!0},a,{type:"datetime",categories:null})}); +a.yAxis=Fa(ja(a.yAxis||{}),function(a){d=a.opposite;return z({labels:{align:d?"right":"left",x:d?-2:2,y:-2},showLastLabel:!1,title:{text:null}},a)});a.series=null;a=z({chart:{panning:!0,pinchType:"x"},navigator:{enabled:!0},scrollbar:{enabled:!0},rangeSelector:{enabled:!0},title:{text:null},tooltip:{shared:!0,crosshairs:!0},legend:{enabled:!1},plotOptions:{line:e,spline:e,area:e,areaspline:e,arearange:e,areasplinerange:e,column:f,columnrange:f,candlestick:f,ohlc:f}},a,{_stock:!0,chart:{inverted:!1}}); +a.series=c;return new Sa(a,b)};sa(pb.prototype,"init",function(a,b,c){var d=c.chart.pinchType||"";a.call(this,b,c);this.pinchX=this.pinchHor=d.indexOf("x")!==-1;this.pinchY=this.pinchVert=d.indexOf("y")!==-1});var kc=V.init,lc=V.processData,mc=Ha.prototype.tooltipFormatter;V.init=function(){kc.apply(this,arguments);this.setCompare(this.options.compare)};V.setCompare=function(a){this.modifyValue=a==="value"||a==="percent"?function(b,c){var d=this.compareValue,b=a==="value"?b-d:b=100*(b/d)-100;if(c)c.change= +b;return b}:null;if(this.chart.hasRendered)this.isDirty=!0};V.processData=function(){lc.apply(this,arguments);if(this.options.compare)for(var a=0,b=this.processedXData,c=this.processedYData,d=c.length,e=this.xAxis.getExtremes().min;a=e){this.compareValue=c[a];break}};Da.prototype.setCompare=function(a,b){this.isXAxis||(n(this.series,function(b){b.setCompare(a)}),p(b,!0)&&this.chart.redraw())};Ha.prototype.tooltipFormatter=function(a){a=a.replace("{point.change}", +(this.change>0?"+":"")+xa(this.change,this.series.tooltipOptions.changeDecimals||2));return mc.apply(this,[a])};(function(){var a=V.init,b=V.getSegments;V.init=function(){var b,d;a.apply(this,arguments);b=this.chart;(d=this.xAxis)&&d.options.ordinal&&F(this,"updatedData",function(){delete d.ordinalIndex});if(d&&d.options.ordinal&&!d.hasOrdinalExtension){d.hasOrdinalExtension=!0;d.beforeSetTickPositions=function(){var a,b=[],c=!1,e,j=this.getExtremes(),k=j.min,j=j.max,m;if(this.options.ordinal){n(this.series, +function(c,d){if(c.visible!==!1&&c.takeOrdinalPosition!==!1&&(b=b.concat(c.processedXData),a=b.length,b.sort(function(a,b){return a-b}),a))for(d=a-1;d--;)b[d]===b[d+1]&&b.splice(d,1)});a=b.length;if(a>2){e=b[1]-b[0];for(m=a-1;m--&&!c;)b[m+1]-b[m]!==e&&(c=!0)}c?(this.ordinalPositions=b,c=d.val2lin(k,!0),e=d.val2lin(j,!0),this.ordinalSlope=j=(j-k)/(e-c),this.ordinalOffset=k-c*j):this.ordinalPositions=this.ordinalSlope=this.ordinalOffset=w}};d.val2lin=function(a,b){var c=this.ordinalPositions;if(c){var d= +c.length,e,k;for(e=d;e--;)if(c[e]===a){k=e;break}for(e=d-1;e--;)if(a>c[e]||e===0){c=(a-c[e])/(c[e+1]-c[e]);k=e+c;break}return b?k:this.ordinalSlope*(k||0)+this.ordinalOffset}else return a};d.lin2val=function(a,b){var c=this.ordinalPositions;if(c){var d=this.ordinalSlope,e=this.ordinalOffset,k=c.length-1,m,l;if(b)a<0?a=c[0]:a>k?a=c[k]:(k=W(a),l=a-k);else for(;k--;)if(m=d*k+e,a>=m){d=d*(k+1)+e;l=(a-m)/(d-m);break}return l!==w&&c[k]!==w?c[k]+(l?l*(c[k+1]-c[k]):0):a}else return a};d.getExtendedPositions= +function(){var a=d.series[0].currentDataGrouping,e=d.ordinalIndex,h=a?a.count+a.unitName:"raw",i=d.getExtremes(),j,k;if(!e)e=d.ordinalIndex={};if(!e[h])j={series:[],getExtremes:function(){return{min:i.dataMin,max:i.dataMax}},options:{ordinal:!0}},n(d.series,function(d){k={xAxis:j,xData:d.xData,chart:b,destroyGroupedData:qa};k.options={dataGrouping:a?{enabled:!0,forced:!0,approximation:"open",units:[[a.unitName,[a.count]]]}:{enabled:!1}};d.processData.apply(k);j.series.push(k)}),d.beforeSetTickPositions.apply(j), +e[h]=j.ordinalPositions;return e[h]};d.getGroupIntervalFactor=function(a,b,c){for(var d=0,e=c.length,k=[];dc;j[n]k*5||t){if(j[n]>z){for(p=fb(a,j[l],j[n],e);p.length&&p[0]<=z;)p.shift();p.length&&(z=p[p.length-1]);y=y.concat(p)}l=n+1}if(t)break}a=p.info;if(m&&a.unitRange<=I[za]){n=y.length-1;for(l=1;lc?a-1:a;for(B=void 0;m--;)l=n[m],c=B-l,B&&c +1)o&&n(o,function(a){a.setState()}),i<0?(o=r,r=d.ordinalPositions?d:r):o=d.ordinalPositions?d:r,u=r.ordinalPositions,k>u[u.length-1]&&u.push(k),o=p.apply(o,[s.apply(o,[m,!0])+i,!0]),i=p.apply(r,[s.apply(r,[l,!0])+i,!0]),o>A(j.dataMin,m)&&ia.xAxis.closestPointRange*e&&d.splice(g+1,0,b.splice(h+1,b.length-h))})}})();y(Highcharts,{Axis:Da,Chart:Sa,Color:wa,Legend:Hb,Pointer:pb,Point:Ha,Tick:ab,Tooltip:Gb,Renderer:cb,Series:$,SVGElement:Ca,SVGRenderer:Ga,arrayMin:Pa,arrayMax:ua,charts:Ua,dateFormat:ya,format:La,pathAnim:Lb,getOptions:function(){return M},hasBidiBug:cc,isTouchDevice:ub,numberFormat:xa,seriesTypes:Q,setOptions:function(a){M=z(M,a);Tb();return M},addEvent:F,removeEvent:X,createElement:aa,discardElement:Za, +css:L,each:n,extend:y,map:Fa,merge:z,pick:p,splat:ja,extendClass:ea,pInt:C,wrap:sa,svg:ca,canvas:ia,vml:!ca&&!ia,product:"Highstock",version:"1.3.2"})})(); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highstock.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highstock.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/highstock.src.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/highstock.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -2,15 +2,15 @@ // @compilation_level SIMPLE_OPTIMIZATIONS /** - * @license Highstock JS v1.1.6 (2012-06-08) + * @license Highstock JS v1.3.2 (2013-06-05) * - * (c) 2009-2011 Torstein Hønsi + * (c) 2009-2013 Torstein Hønsi * * License: www.highcharts.com/license */ // JSLint options: -/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console */ +/*global Highcharts, document, window, navigator, setInterval, clearInterval, clearTimeout, setTimeout, location, jQuery, $, console, each, grep */ (function () { // encapsulated variables @@ -32,10 +32,12 @@ // some variables userAgent = navigator.userAgent, - isIE = /msie/i.test(userAgent) && !win.opera, + isOpera = win.opera, + isIE = /msie/i.test(userAgent) && !isOpera, docMode8 = doc.documentMode === 8, isWebKit = /AppleWebKit/.test(userAgent), isFirefox = /Firefox/.test(userAgent), + isTouchDevice = /(Mobile|Android|Windows Phone)/.test(userAgent), SVG_NS = 'http://www.w3.org/2000/svg', hasSVG = !!doc.createElementNS && !!doc.createElementNS(SVG_NS, 'svg').createSVGRect, hasBidiBug = isFirefox && parseInt(userAgent.split('Firefox/')[1], 10) < 4, // issue #38 @@ -51,6 +53,9 @@ pathAnim, timeUnits, noop = function () {}, + charts = [], + PRODUCT = 'Highstock', + VERSION = '1.3.2', // some constants for frequently used strings DIV = 'div', @@ -69,12 +74,13 @@ * IE7: 0.002 * IE8: 0.002 * IE9: 0.00000000001 (unlimited) + * IE10: 0.0001 (exporting only) * FF: 0.00000000001 (unlimited) * Chrome: 0.000001 * Safari: 0.000001 * Opera: 0.00000000001 (unlimited) */ - TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.000001 : 0.002) + ')', // invisible but clickable + TRACKER_FILL = 'rgba(192,192,192,' + (hasSVG ? 0.0001 : 0.002) + ')', // invisible but clickable //TRACKER_FILL = 'rgba(192,192,192,0.5)', NORMAL_STATE = '', HOVER_STATE = 'hover', @@ -89,10 +95,8 @@ YEAR = 'year', // constants for attributes - FILL = 'fill', LINEAR_GRADIENT = 'linearGradient', STOPS = 'stops', - STROKE = 'stroke', STROKE_WIDTH = 'stroke-width', // time methods, changed based on whether or not UTC is used @@ -114,7 +118,7 @@ seriesTypes = {}; // The Highcharts namespace -win.Highcharts = {}; +win.Highcharts = win.Highcharts ? error(16, true) : {}; /** * Extend an object with the members of another @@ -131,6 +135,50 @@ } return a; } + +/** + * Deep merge two or more objects and return a third object. + * Previously this function redirected to jQuery.extend(true), but this had two limitations. + * First, it deep merged arrays, which lead to workarounds in Highcharts. Second, + * it copied properties from extended prototypes. + */ +function merge() { + var i, + len = arguments.length, + ret = {}, + doCopy = function (copy, original) { + var value, key; + + for (key in original) { + if (original.hasOwnProperty(key)) { + value = original[key]; + + // An object is replacing a primitive + if (typeof copy !== 'object') { + copy = {}; + } + + // Copy the contents of objects, but not arrays or DOM nodes + if (value && typeof value === 'object' && Object.prototype.toString.call(value) !== '[object Array]' + && typeof value.nodeType !== 'number') { + copy[key] = doCopy(copy[key] || {}, value); + + // Primitives and arrays are copied over directly + } else { + copy[key] = original[key]; + } + } + } + return copy; + }; + + // For each argument, extend the return + for (i = 0; i < len; i++) { + ret = doCopy(ret, arguments[i]); + } + + return ret; +} /** * Take an array and turn into a hash with even number arguments as keys and odd numbers as @@ -330,18 +378,6 @@ } /** - * How many decimals are there in a number - */ -function getDecimals(number) { - - number = (number || 0).toString(); - - return number.indexOf('.') > -1 ? - number.split('.')[1].length : - 0; -} - -/** * Format a number and return a string based on input settings * @param {Number} number The input number to format * @param {Number} decimals The amount of decimals @@ -353,7 +389,7 @@ // http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/ n = number, c = decimals === -1 ? - getDecimals(number) : + ((n || 0).toString().split('.')[1] || '').length : // preserve decimals (isNaN(decimals = mathAbs(decimals)) ? 2 : decimals), d = decPoint === undefined ? lang.decimalPoint : decPoint, t = thousandsSep === undefined ? lang.thousandsSep : thousandsSep, @@ -376,6 +412,23 @@ } /** + * Wrap a method with extended functionality, preserving the original function + * @param {Object} obj The context object that the method belongs to + * @param {String} method The name of the method to extend + * @param {Function} func A wrapper function callback. This function is called with the same arguments + * as the original function, except that the original function is unshifted and passed as the first + * argument. + */ +function wrap(obj, method, func) { + var proceed = obj[method]; + obj[method] = function () { + var args = Array.prototype.slice.call(arguments); + args.unshift(proceed); + return func.apply(this, args); + }; +} + +/** * Based on http://www.php.net/manual/en/function.strftime.php * @param {String} format * @param {Number} timestamp @@ -397,19 +450,9 @@ fullYear = date[getFullYear](), lang = defaultOptions.lang, langWeekdays = lang.weekdays, - /* // uncomment this and the 'W' format key below to enable week numbers - weekNumber = function () { - var clone = new Date(date.valueOf()), - day = clone[getDay]() == 0 ? 7 : clone[getDay](), - dayNumber; - clone.setDate(clone[getDate]() + 4 - day); - dayNumber = mathFloor((clone.getTime() - new Date(clone[getFullYear](), 0, 1, -6)) / 86400000); - return 1 + mathFloor(dayNumber / 7); - }, - */ - // list all format keys - replacements = { + // List all format keys. Custom formats can be added from the outside. + replacements = extend({ // Day 'a': langWeekdays[day].substr(0, 3), // Short weekday, like 'Mon' @@ -438,18 +481,94 @@ 'P': hours < 12 ? 'am' : 'pm', // Lower case AM or PM 'S': pad(date.getSeconds()), // Two digits seconds, 00 through 59 'L': pad(mathRound(timestamp % 1000), 3) // Milliseconds (naming from Ruby) - }; + }, Highcharts.dateFormats); // do the replaces for (key in replacements) { - format = format.replace('%' + key, replacements[key]); + while (format.indexOf('%' + key) !== -1) { // regex would do it in one line, but this is faster + format = format.replace('%' + key, typeof replacements[key] === 'function' ? replacements[key](timestamp) : replacements[key]); + } } // Optionally capitalize the string and return return capitalize ? format.substr(0, 1).toUpperCase() + format.substr(1) : format; }; +/** + * Format a single variable. Similar to sprintf, without the % prefix. + */ +function formatSingle(format, val) { + var floatRegex = /f$/, + decRegex = /\.([0-9])/, + lang = defaultOptions.lang, + decimals; + + if (floatRegex.test(format)) { // float + decimals = format.match(decRegex); + decimals = decimals ? decimals[1] : -1; + val = numberFormat( + val, + decimals, + lang.decimalPoint, + format.indexOf(',') > -1 ? lang.thousandsSep : '' + ); + } else { + val = dateFormat(format, val); + } + return val; +} + +/** + * Format a string according to a subset of the rules of Python's String.format method. + */ +function format(str, ctx) { + var splitter = '{', + isInside = false, + segment, + valueAndFormat, + path, + i, + len, + ret = [], + val, + index; + + while ((index = str.indexOf(splitter)) !== -1) { + + segment = str.slice(0, index); + if (isInside) { // we're on the closing bracket looking back + + valueAndFormat = segment.split(':'); + path = valueAndFormat.shift().split('.'); // get first and leave format + len = path.length; + val = ctx; + + // Assign deeper paths + for (i = 0; i < len; i++) { + val = val[path[i]]; + } + + // Format the replacement + if (valueAndFormat.length) { + val = formatSingle(valueAndFormat.join(':'), val); + } + + // Push the result and advance the cursor + ret.push(val); + + } else { + ret.push(segment); + + } + str = str.slice(index + 1); // the rest + isInside = !isInside; // toggle + splitter = isInside ? '}' : '{'; // now look for next matching bracket + } + ret.push(str); + return ret.join(''); +} + /** * Take an interval and normalize it to multiples of 1, 2, 2.5 and 5 * @param {Number} interval @@ -592,91 +711,95 @@ interval = normalizedInterval.unitRange, count = normalizedInterval.count; + if (defined(min)) { // #1300 + if (interval >= timeUnits[SECOND]) { // second + minDate.setMilliseconds(0); + minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 : + count * mathFloor(minDate.getSeconds() / count)); + } - - if (interval >= timeUnits[SECOND]) { // second - minDate.setMilliseconds(0); - minDate.setSeconds(interval >= timeUnits[MINUTE] ? 0 : - count * mathFloor(minDate.getSeconds() / count)); - } - - if (interval >= timeUnits[MINUTE]) { // minute - minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 : - count * mathFloor(minDate[getMinutes]() / count)); - } - - if (interval >= timeUnits[HOUR]) { // hour - minDate[setHours](interval >= timeUnits[DAY] ? 0 : - count * mathFloor(minDate[getHours]() / count)); - } - - if (interval >= timeUnits[DAY]) { // day - minDate[setDate](interval >= timeUnits[MONTH] ? 1 : - count * mathFloor(minDate[getDate]() / count)); - } - - if (interval >= timeUnits[MONTH]) { // month - minDate[setMonth](interval >= timeUnits[YEAR] ? 0 : - count * mathFloor(minDate[getMonth]() / count)); + if (interval >= timeUnits[MINUTE]) { // minute + minDate[setMinutes](interval >= timeUnits[HOUR] ? 0 : + count * mathFloor(minDate[getMinutes]() / count)); + } + + if (interval >= timeUnits[HOUR]) { // hour + minDate[setHours](interval >= timeUnits[DAY] ? 0 : + count * mathFloor(minDate[getHours]() / count)); + } + + if (interval >= timeUnits[DAY]) { // day + minDate[setDate](interval >= timeUnits[MONTH] ? 1 : + count * mathFloor(minDate[getDate]() / count)); + } + + if (interval >= timeUnits[MONTH]) { // month + minDate[setMonth](interval >= timeUnits[YEAR] ? 0 : + count * mathFloor(minDate[getMonth]() / count)); + minYear = minDate[getFullYear](); + } + + if (interval >= timeUnits[YEAR]) { // year + minYear -= minYear % count; + minDate[setFullYear](minYear); + } + + // week is a special case that runs outside the hierarchy + if (interval === timeUnits[WEEK]) { + // get start of current week, independent of count + minDate[setDate](minDate[getDate]() - minDate[getDay]() + + pick(startOfWeek, 1)); + } + + + // get tick positions + i = 1; minYear = minDate[getFullYear](); - } - - if (interval >= timeUnits[YEAR]) { // year - minYear -= minYear % count; - minDate[setFullYear](minYear); - } - - // week is a special case that runs outside the hierarchy - if (interval === timeUnits[WEEK]) { - // get start of current week, independent of count - minDate[setDate](minDate[getDate]() - minDate[getDay]() + - pick(startOfWeek, 1)); - } - - - // get tick positions - i = 1; - minYear = minDate[getFullYear](); - var time = minDate.getTime(), - minMonth = minDate[getMonth](), - minDateDate = minDate[getDate](), - timezoneOffset = useUTC ? - 0 : - (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950 - - // iterate and add tick positions at appropriate values - while (time < max) { - tickPositions.push(time); - - // if the interval is years, use Date.UTC to increase years - if (interval === timeUnits[YEAR]) { - time = makeTime(minYear + i * count, 0); - - // if the interval is months, use Date.UTC to increase months - } else if (interval === timeUnits[MONTH]) { - time = makeTime(minYear, minMonth + i * count); - - // if we're using global time, the interval is not fixed as it jumps - // one hour at the DST crossover - } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) { - time = makeTime(minYear, minMonth, minDateDate + - i * count * (interval === timeUnits[DAY] ? 1 : 7)); - - // else, the interval is fixed and we use simple addition - } else { - time += interval * count; - - // mark new days if the time is dividable by day - if (interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset) { - higherRanks[time] = DAY; + var time = minDate.getTime(), + minMonth = minDate[getMonth](), + minDateDate = minDate[getDate](), + timezoneOffset = useUTC ? + 0 : + (24 * 3600 * 1000 + minDate.getTimezoneOffset() * 60 * 1000) % (24 * 3600 * 1000); // #950 + + // iterate and add tick positions at appropriate values + while (time < max) { + tickPositions.push(time); + + // if the interval is years, use Date.UTC to increase years + if (interval === timeUnits[YEAR]) { + time = makeTime(minYear + i * count, 0); + + // if the interval is months, use Date.UTC to increase months + } else if (interval === timeUnits[MONTH]) { + time = makeTime(minYear, minMonth + i * count); + + // if we're using global time, the interval is not fixed as it jumps + // one hour at the DST crossover + } else if (!useUTC && (interval === timeUnits[DAY] || interval === timeUnits[WEEK])) { + time = makeTime(minYear, minMonth, minDateDate + + i * count * (interval === timeUnits[DAY] ? 1 : 7)); + + // else, the interval is fixed and we use simple addition + } else { + time += interval * count; } + + i++; } + + // push the last time + tickPositions.push(time); - i++; + + // mark new days if the time is dividible by day (#1649, #1760) + each(grep(tickPositions, function (time) { + return interval <= timeUnits[HOUR] && time % timeUnits[DAY] === timezoneOffset; + }), function (time) { + higherRanks[time] = DAY; + }); } - - // push the last time - tickPositions.push(time); + // record information on the chosen unit - for dynamic label formatter tickPositions.info = extend(normalizedInterval, { @@ -837,6 +960,16 @@ } /** + * Set the global animation to either a given value, or fall back to the + * given chart's animation option + * @param {Object} animation + * @param {Object} chart + */ +function setAnimation(animation, chart) { + globalAnimation = pick(animation, chart.animation); +} + +/** * The time unit lookup */ /*jslint white: true*/ @@ -847,7 +980,7 @@ HOUR, 3600000, DAY, 24 * 3600000, WEEK, 7 * 24 * 3600000, - MONTH, 30 * 24 * 3600000, + MONTH, 31 * 24 * 3600000, YEAR, 31556952000 ); /*jslint white: false*/ @@ -947,33 +1080,371 @@ } }; +(function ($) { + /** + * The default HighchartsAdapter for jQuery + */ + win.HighchartsAdapter = win.HighchartsAdapter || ($ && { + + /** + * Initialize the adapter by applying some extensions to jQuery + */ + init: function (pathAnim) { + + // extend the animate function to allow SVG animations + var Fx = $.fx, + Step = Fx.step, + dSetter, + Tween = $.Tween, + propHooks = Tween && Tween.propHooks, + opacityHook = $.cssHooks.opacity; + + /*jslint unparam: true*//* allow unused param x in this function */ + $.extend($.easing, { + easeOutQuad: function (x, t, b, c, d) { + return -c * (t /= d) * (t - 2) + b; + } + }); + /*jslint unparam: false*/ + + // extend some methods to check for elem.attr, which means it is a Highcharts SVG object + $.each(['cur', '_default', 'width', 'height', 'opacity'], function (i, fn) { + var obj = Step, + base, + elem; + + // Handle different parent objects + if (fn === 'cur') { + obj = Fx.prototype; // 'cur', the getter, relates to Fx.prototype + + } else if (fn === '_default' && Tween) { // jQuery 1.8 model + obj = propHooks[fn]; + fn = 'set'; + } + + // Overwrite the method + base = obj[fn]; + if (base) { // step.width and step.height don't exist in jQuery < 1.7 + + // create the extended function replacement + obj[fn] = function (fx) { + + // Fx.prototype.cur does not use fx argument + fx = i ? fx : this; + + // shortcut + elem = fx.elem; + + // Fx.prototype.cur returns the current value. The other ones are setters + // and returning a value has no effect. + return elem.attr ? // is SVG element wrapper + elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method + base.apply(this, arguments); // use jQuery's built-in method + }; + } + }); + + // Extend the opacity getter, needed for fading opacity with IE9 and jQuery 1.10+ + wrap(opacityHook, 'get', function (proceed, elem, computed) { + return elem.attr ? (elem.opacity || 0) : proceed.call(this, elem, computed); + }); + + + // Define the setter function for d (path definitions) + dSetter = function (fx) { + var elem = fx.elem, + ends; + + // Normally start and end should be set in state == 0, but sometimes, + // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped + // in these cases + if (!fx.started) { + ends = pathAnim.init(elem, elem.d, elem.toD); + fx.start = ends[0]; + fx.end = ends[1]; + fx.started = true; + } + + + // interpolate each value of the path + elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD)); + }; + + // jQuery 1.8 style + if (Tween) { + propHooks.d = { + set: dSetter + }; + // pre 1.8 + } else { + // animate paths + Step.d = dSetter; + } + + /** + * Utility for iterating over an array. Parameters are reversed compared to jQuery. + * @param {Array} arr + * @param {Function} fn + */ + this.each = Array.prototype.forEach ? + function (arr, fn) { // modern browsers + return Array.prototype.forEach.call(arr, fn); + + } : + function (arr, fn) { // legacy + var i = 0, + len = arr.length; + for (; i < len; i++) { + if (fn.call(arr[i], arr[i], i, arr) === false) { + return i; + } + } + }; + + /** + * Register Highcharts as a plugin in the respective framework + */ + $.fn.highcharts = function () { + var constr = 'Chart', // default constructor + args = arguments, + options, + ret, + chart; + + if (isString(args[0])) { + constr = args[0]; + args = Array.prototype.slice.call(args, 1); + } + options = args[0]; + + // Create the chart + if (options !== UNDEFINED) { + /*jslint unused:false*/ + options.chart = options.chart || {}; + options.chart.renderTo = this[0]; + chart = new Highcharts[constr](options, args[1]); + ret = this; + /*jslint unused:true*/ + } + + // When called without parameters or with the return argument, get a predefined chart + if (options === UNDEFINED) { + ret = charts[attr(this[0], 'data-highcharts-chart')]; + } -/** - * Set the global animation to either a given value, or fall back to the - * given chart's animation option - * @param {Object} animation - * @param {Object} chart - */ -function setAnimation(animation, chart) { - globalAnimation = pick(animation, chart.animation); -} + return ret; + }; + }, + + + /** + * Downloads a script and executes a callback when done. + * @param {String} scriptLocation + * @param {Function} callback + */ + getScript: $.getScript, + + /** + * Return the index of an item in an array, or -1 if not found + */ + inArray: $.inArray, + + /** + * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method. + * @param {Object} elem The HTML element + * @param {String} method Which method to run on the wrapped element + */ + adapterRun: function (elem, method) { + return $(elem)[method](); + }, + + /** + * Filter an array + */ + grep: $.grep, + + /** + * Map an array + * @param {Array} arr + * @param {Function} fn + */ + map: function (arr, fn) { + //return jQuery.map(arr, fn); + var results = [], + i = 0, + len = arr.length; + for (; i < len; i++) { + results[i] = fn.call(arr[i], arr[i], i, arr); + } + return results; + + }, + + /** + * Get the position of an element relative to the top left of the page + */ + offset: function (el) { + return $(el).offset(); + }, + + /** + * Add an event listener + * @param {Object} el A HTML element or custom object + * @param {String} event The event type + * @param {Function} fn The event handler + */ + addEvent: function (el, event, fn) { + $(el).bind(event, fn); + }, + + /** + * Remove event added with addEvent + * @param {Object} el The object + * @param {String} eventType The event type. Leave blank to remove all events. + * @param {Function} handler The function to remove + */ + removeEvent: function (el, eventType, handler) { + // workaround for jQuery issue with unbinding custom events: + // http://forum.jQuery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jQuery-1-4-2 + var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent'; + if (doc[func] && el && !el[func]) { + el[func] = function () {}; + } + + $(el).unbind(eventType, handler); + }, + + /** + * Fire an event on a custom object + * @param {Object} el + * @param {String} type + * @param {Object} eventArguments + * @param {Function} defaultFunction + */ + fireEvent: function (el, type, eventArguments, defaultFunction) { + var event = $.Event(type), + detachedType = 'detached' + type, + defaultPrevented; + + // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts + // never uses these properties, Chrome includes them in the default click event and + // raises the warning when they are copied over in the extend statement below. + // + // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid + // testing if they are there (warning in chrome) the only option is to test if running IE. + if (!isIE && eventArguments) { + delete eventArguments.layerX; + delete eventArguments.layerY; + } + + extend(event, eventArguments); + + // Prevent jQuery from triggering the object method that is named the + // same as the event. For example, if the event is 'select', jQuery + // attempts calling el.select and it goes into a loop. + if (el[type]) { + el[detachedType] = el[type]; + el[type] = null; + } + + // Wrap preventDefault and stopPropagation in try/catch blocks in + // order to prevent JS errors when cancelling events on non-DOM + // objects. #615. + /*jslint unparam: true*/ + $.each(['preventDefault', 'stopPropagation'], function (i, fn) { + var base = event[fn]; + event[fn] = function () { + try { + base.call(event); + } catch (e) { + if (fn === 'preventDefault') { + defaultPrevented = true; + } + } + }; + }); + /*jslint unparam: false*/ + + // trigger it + $(el).trigger(event); + + // attach the method + if (el[detachedType]) { + el[type] = el[detachedType]; + el[detachedType] = null; + } + + if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) { + defaultFunction(event); + } + }, + + /** + * Extension method needed for MooTools + */ + washMouseEvent: function (e) { + var ret = e.originalEvent || e; + + // computed by jQuery, needed by IE8 + if (ret.pageX === UNDEFINED) { // #1236 + ret.pageX = e.pageX; + ret.pageY = e.pageY; + } + + return ret; + }, + + /** + * Animate a HTML element or SVG element wrapper + * @param {Object} el + * @param {Object} params + * @param {Object} options jQuery-like animation options: duration, easing, callback + */ + animate: function (el, params, options) { + var $el = $(el); + if (!el.style) { + el.style = {}; // #1881 + } + if (params.d) { + el.toD = params.d; // keep the array form for paths, used in $.fx.step.d + params.d = 1; // because in jQuery, animating to an array has a different meaning + } + + $el.stop(); + $el.animate(params, options); + + }, + /** + * Stop running animation + */ + stop: function (el) { + $(el).stop(); + } + }); +}(win.jQuery)); // check for a custom HighchartsAdapter defined prior to this file var globalAdapter = win.HighchartsAdapter, - adapter = globalAdapter || {}, + adapter = globalAdapter || {}; + +// Initialize the adapter +if (globalAdapter) { + globalAdapter.init.call(globalAdapter, pathAnim); +} + - // Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object - // and all the utility functions will be null. In that case they are populated by the - // default adapters below. - adapterRun = adapter.adapterRun, +// Utility functions. If the HighchartsAdapter is not defined, adapter is an empty object +// and all the utility functions will be null. In that case they are populated by the +// default adapters below. +var adapterRun = adapter.adapterRun, getScript = adapter.getScript, + inArray = adapter.inArray, each = adapter.each, grep = adapter.grep, offset = adapter.offset, map = adapter.map, - merge = adapter.merge, addEvent = adapter.addEvent, removeEvent = adapter.removeEvent, fireEvent = adapter.fireEvent, @@ -981,285 +1452,12 @@ animate = adapter.animate, stop = adapter.stop; -/* - * Define the adapter for frameworks. If an external adapter is not defined, - * Highcharts reverts to the built-in jQuery adapter. - */ -if (globalAdapter && globalAdapter.init) { - // Initialize the adapter with the pathAnim object that takes care - // of path animations. - globalAdapter.init(pathAnim); -} -if (!globalAdapter && win.jQuery) { - var jQ = jQuery; - - /** - * Downloads a script and executes a callback when done. - * @param {String} scriptLocation - * @param {Function} callback - */ - getScript = jQ.getScript; - - /** - * A direct link to jQuery methods. MooTools and Prototype adapters must be implemented for each case of method. - * @param {Object} elem The HTML element - * @param {String} method Which method to run on the wrapped element - */ - adapterRun = function (elem, method) { - return jQ(elem)[method](); - }; - /** - * Utility for iterating over an array. Parameters are reversed compared to jQuery. - * @param {Array} arr - * @param {Function} fn - */ - each = function (arr, fn) { - var i = 0, - len = arr.length; - for (; i < len; i++) { - if (fn.call(arr[i], arr[i], i, arr) === false) { - return i; - } - } - }; - /** - * Filter an array - */ - grep = jQ.grep; - - /** - * Map an array - * @param {Array} arr - * @param {Function} fn - */ - map = function (arr, fn) { - //return jQuery.map(arr, fn); - var results = [], - i = 0, - len = arr.length; - for (; i < len; i++) { - results[i] = fn.call(arr[i], arr[i], i, arr); - } - return results; - - }; - - /** - * Deep merge two objects and return a third object - */ - merge = function () { - var args = arguments; - return jQ.extend(true, null, args[0], args[1], args[2], args[3]); - }; - - /** - * Get the position of an element relative to the top left of the page - */ - offset = function (el) { - return jQ(el).offset(); - }; - - /** - * Add an event listener - * @param {Object} el A HTML element or custom object - * @param {String} event The event type - * @param {Function} fn The event handler - */ - addEvent = function (el, event, fn) { - jQ(el).bind(event, fn); - }; - - /** - * Remove event added with addEvent - * @param {Object} el The object - * @param {String} eventType The event type. Leave blank to remove all events. - * @param {Function} handler The function to remove - */ - removeEvent = function (el, eventType, handler) { - // workaround for jQuery issue with unbinding custom events: - // http://forum.jquery.com/topic/javascript-error-when-unbinding-a-custom-event-using-jquery-1-4-2 - var func = doc.removeEventListener ? 'removeEventListener' : 'detachEvent'; - if (doc[func] && !el[func]) { - el[func] = function () {}; - } - - jQ(el).unbind(eventType, handler); - }; - - /** - * Fire an event on a custom object - * @param {Object} el - * @param {String} type - * @param {Object} eventArguments - * @param {Function} defaultFunction - */ - fireEvent = function (el, type, eventArguments, defaultFunction) { - var event = jQ.Event(type), - detachedType = 'detached' + type, - defaultPrevented; - - // Remove warnings in Chrome when accessing layerX and layerY. Although Highcharts - // never uses these properties, Chrome includes them in the default click event and - // raises the warning when they are copied over in the extend statement below. - // - // To avoid problems in IE (see #1010) where we cannot delete the properties and avoid - // testing if they are there (warning in chrome) the only option is to test if running IE. - if (!isIE && eventArguments) { - delete eventArguments.layerX; - delete eventArguments.layerY; - } - - extend(event, eventArguments); - - // Prevent jQuery from triggering the object method that is named the - // same as the event. For example, if the event is 'select', jQuery - // attempts calling el.select and it goes into a loop. - if (el[type]) { - el[detachedType] = el[type]; - el[type] = null; - } - - // Wrap preventDefault and stopPropagation in try/catch blocks in - // order to prevent JS errors when cancelling events on non-DOM - // objects. #615. - each(['preventDefault', 'stopPropagation'], function (fn) { - var base = event[fn]; - event[fn] = function () { - try { - base.call(event); - } catch (e) { - if (fn === 'preventDefault') { - defaultPrevented = true; - } - } - }; - }); - - // trigger it - jQ(el).trigger(event); - - // attach the method - if (el[detachedType]) { - el[type] = el[detachedType]; - el[detachedType] = null; - } - - if (defaultFunction && !event.isDefaultPrevented() && !defaultPrevented) { - defaultFunction(event); - } - }; - - /** - * Extension method needed for MooTools - */ - washMouseEvent = function (e) { - return e; - }; - - /** - * Animate a HTML element or SVG element wrapper - * @param {Object} el - * @param {Object} params - * @param {Object} options jQuery-like animation options: duration, easing, callback - */ - animate = function (el, params, options) { - var $el = jQ(el); - if (params.d) { - el.toD = params.d; // keep the array form for paths, used in jQ.fx.step.d - params.d = 1; // because in jQuery, animating to an array has a different meaning - } - - $el.stop(); - $el.animate(params, options); - - }; - /** - * Stop running animation - */ - stop = function (el) { - jQ(el).stop(); - }; - - - //=== Extend jQuery on init - - /*jslint unparam: true*//* allow unused param x in this function */ - jQ.extend(jQ.easing, { - easeOutQuad: function (x, t, b, c, d) { - return -c * (t /= d) * (t - 2) + b; - } - }); - /*jslint unparam: false*/ - - // extend the animate function to allow SVG animations - var jFx = jQ.fx, - jStep = jFx.step; - - // extend some methods to check for elem.attr, which means it is a Highcharts SVG object - each(['cur', '_default', 'width', 'height'], function (fn, i) { - var obj = jStep, - base, - elem; - - // Handle different parent objects - if (fn === 'cur') { - obj = jFx.prototype; // 'cur', the getter, relates to jFx.prototype - - } else if (fn === '_default' && jQ.Tween) { // jQuery 1.8 model - obj = jQ.Tween.propHooks[fn]; - fn = 'set'; - } - - // Overwrite the method - base = obj[fn]; - if (base) { // step.width and step.height don't exist in jQuery < 1.7 - - // create the extended function replacement - obj[fn] = function (fx) { - - // jFx.prototype.cur does not use fx argument - fx = i ? fx : this; - - // shortcut - elem = fx.elem; - - // jFX.prototype.cur returns the current value. The other ones are setters - // and returning a value has no effect. - return elem.attr ? // is SVG element wrapper - elem.attr(fx.prop, fn === 'cur' ? UNDEFINED : fx.now) : // apply the SVG wrapper's method - base.apply(this, arguments); // use jQuery's built-in method - }; - } - }); - - // animate paths - jStep.d = function (fx) { - var elem = fx.elem; - - - // Normally start and end should be set in state == 0, but sometimes, - // for reasons unknown, this doesn't happen. Perhaps state == 0 is skipped - // in these cases - if (!fx.started) { - var ends = pathAnim.init(elem, elem.d, elem.toD); - fx.start = ends[0]; - fx.end = ends[1]; - fx.started = true; - } - - - // interpolate each value of the path - elem.attr('d', pathAnim.step(fx.start, fx.end, fx.pos, elem.toD)); - - }; -} - -/* **************************************************************************** - * Handle the options * - *****************************************************************************/ -var +/* **************************************************************************** + * Handle the options * + *****************************************************************************/ +var defaultLabelOptions = { enabled: true, @@ -1272,14 +1470,15 @@ },*/ style: { color: '#666', + cursor: 'default', fontSize: '11px', lineHeight: '14px' } }; defaultOptions = { - colors: ['#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE', - '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92'], + colors: ['#2f7ed8', '#0d233a', '#8bbc21', '#910000', '#1aadce', '#492970', + '#f28f43', '#77a1e5', '#c42525', '#a6c96a'], symbols: ['circle', 'diamond', 'square', 'triangle', 'triangle-down'], lang: { loading: 'Loading...', @@ -1288,13 +1487,15 @@ shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], decimalPoint: '.', + numericSymbols: ['k', 'M', 'G', 'T', 'P', 'E'], // SI prefixes used in axis labels resetZoom: 'Reset zoom', resetZoomTitle: 'Reset zoom level 1:1', thousandsSep: ',' }, global: { useUTC: true, - canvasToolsURL: 'http://code.highcharts.com/stock/1.1.6/modules/canvas-tools.js' + canvasToolsURL: 'http://code.highcharts.com/stock/1.3.2/modules/canvas-tools.js', + VMLRadialGradientURL: 'http://code.highcharts.com/stock/1.3.2/gfx/vml-radial-gradient.png' }, chart: { //animation: true, @@ -1350,7 +1551,7 @@ // verticalAlign: 'top', y: 15, style: { - color: '#3E576F', + color: '#274b6d',//#3E576F', fontSize: '16px' } @@ -1363,7 +1564,7 @@ // verticalAlign: 'top', y: 30, style: { - color: '#6D869F' + color: '#4d759e' } }, @@ -1382,7 +1583,7 @@ events: {}, //legendIndex: 0, lineWidth: 2, - shadow: true, + //shadow: false, // stacking: null, marker: { enabled: true, @@ -1393,6 +1594,7 @@ //fillColor: null, states: { // states for a single point hover: { + enabled: true //radius: base + 2 }, select: { @@ -1407,10 +1609,11 @@ }, dataLabels: merge(defaultLabelOptions, { enabled: false, - y: -6, formatter: function () { - return this.y; - } + return numberFormat(this.y, -1); + }, + verticalAlign: 'bottom', // above singular point + y: 0 // backgroundColor: undefined, // borderColor: undefined, // borderRadius: undefined, @@ -1469,7 +1672,7 @@ borderRadius: 5, navigation: { // animation: true, - activeColor: '#3E576F', + activeColor: '#274b6d', // arrowSize: 12 inactiveColor: '#CCC' // style: {} // text styles @@ -1483,7 +1686,7 @@ },*/ itemStyle: { cursor: 'pointer', - color: '#3E576F', + color: '#274b6d', fontSize: '12px' }, itemHoverStyle: { @@ -1504,7 +1707,13 @@ verticalAlign: 'bottom', // width: undefined, x: 0, - y: 0 + y: 0, + title: { + //text: null, + style: { + fontWeight: 'bold' + } + } }, loading: { @@ -1525,10 +1734,11 @@ tooltip: { enabled: true, + animation: hasSVG, //crosshairs: null, backgroundColor: 'rgba(255, 255, 255, .85)', - borderWidth: 2, - borderRadius: 5, + borderWidth: 1, + borderRadius: 3, dateTimeLabelFormats: { millisecond: '%A, %b %e, %H:%M:%S.%L', second: '%A, %b %e, %H:%M:%S', @@ -1543,12 +1753,13 @@ headerFormat: '{point.key}
    ', pointFormat: '{series.name}: {point.y}
    ', shadow: true, - shared: useCanVG, - snap: hasTouch ? 25 : 10, + //shared: false, + snap: isTouchDevice ? 25 : 10, style: { color: '#333333', + cursor: 'default', fontSize: '12px', - padding: '5px', + padding: '8px', whiteSpace: 'nowrap' } //xDateFormat: '%A, %b %e, %Y', @@ -1570,7 +1781,7 @@ style: { cursor: 'pointer', color: '#909090', - fontSize: '10px' + fontSize: '9px' } } }; @@ -1649,14 +1860,13 @@ } - /** * Handle color operations. The object methods are chainable. * @param {String} input The input color in either rbga or hex format */ var Color = function (input) { // declare variables - var rgba = [], result; + var rgba = [], result, stops; /** * Parse the input color to rgba array @@ -1664,16 +1874,32 @@ */ function init(input) { - // rgba - 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(input); - if (result) { - rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)]; - } else { // hex - result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input); + // Gradients + if (input && input.stops) { + stops = map(input.stops, function (stop) { + return Color(stop[1]); + }); + + // Solid colors + } else { + // rgba + 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(input); if (result) { - rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1]; + rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), parseFloat(result[4], 10)]; + } else { + // hex + result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(input); + if (result) { + rgba = [pInt(result[1], 16), pInt(result[2], 16), pInt(result[3], 16), 1]; + } else { + // rgb + result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(input); + if (result) { + rgba = [pInt(result[1]), pInt(result[2]), pInt(result[3]), 1]; + } + } } - } + } } /** @@ -1683,8 +1909,15 @@ function get(format) { var ret; + if (stops) { + ret = merge(input); + ret.stops = [].concat(ret.stops); + each(stops, function (stop, i) { + ret.stops[i] = [ret.stops[i][0], stop.get(format)]; + }); + // it's NaN if gradient colors on a column chart - if (rgba && !isNaN(rgba[0])) { + } else if (rgba && !isNaN(rgba[0])) { if (format === 'rgb') { ret = 'rgb(' + rgba[0] + ',' + rgba[1] + ',' + rgba[2] + ')'; } else if (format === 'a') { @@ -1703,7 +1936,12 @@ * @param {Number} alpha */ function brighten(alpha) { - if (isNumber(alpha) && alpha !== 0) { + if (stops) { + each(stops, function (stop) { + stop.brighten(alpha); + }); + + } else if (isNumber(alpha) && alpha !== 0) { var i; for (i = 0; i < 3; i++) { rgba[i] += pInt(alpha * 255); @@ -1734,6 +1972,7 @@ return { get: get, brighten: brighten, + rgba: rgba, setOpacity: setOpacity }; }; @@ -1765,6 +2004,10 @@ wrapper.attrSetters = {}; }, /** + * Default base for animation + */ + opacity: 1, + /** * Animate a given attribute * @param {Object} params * @param {Number} options The same options as in jQuery animation @@ -1799,7 +2042,7 @@ i, child, element = wrapper.element, - nodeName = element.nodeName, + nodeName = element.nodeName.toLowerCase(), // Android2 requires lower for "text" renderer = wrapper.renderer, skipAttr, titleNode, @@ -1825,7 +2068,6 @@ key = 'stroke-width'; } ret = attr(element, key) || wrapper[key] || 0; - if (key !== 'd' && key !== 'visibility') { // 'd' is string in animation step ret = parseFloat(ret); } @@ -1838,13 +2080,14 @@ value = hash[key]; // check for a specific attribute setter - result = attrSetters[key] && attrSetters[key](value, key); + result = attrSetters[key] && attrSetters[key].call(wrapper, value, key); if (result !== false) { if (result !== UNDEFINED) { value = result; // the attribute setter has returned a new value to set } + // paths if (key === 'd') { if (value && value.join) { // join path @@ -1866,10 +2109,8 @@ } } - if (wrapper.rotation) { - attr(element, 'transform', 'rotate(' + wrapper.rotation + ' ' + value + ' ' + - pInt(hash.y || attr(element, 'y')) + ')'); - } + } else if (wrapper.rotation && (key === 'x' || key === 'y')) { + doTransform = true; // apply gradients } else if (key === 'fill') { @@ -1888,7 +2129,8 @@ skipAttr = true; // translation and text rotation - } else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || key === 'verticalAlign') { + } else if (key === 'translateX' || key === 'translateY' || key === 'rotation' || + key === 'verticalAlign' || key === 'scaleX' || key === 'scaleY') { doTransform = true; skipAttr = true; @@ -1921,10 +2163,6 @@ value = value.join(','); } - // special - } else if (key === 'isTracker') { - wrapper[key] = value; - // IE9/MooTools combo: MooTools returns objects instead of numbers and IE9 Beta 2 // is unable to cast them. Test again with final IE9. } else if (key === 'width') { @@ -1950,9 +2188,20 @@ key = 'stroke-width'; } - // Chrome/Win < 6 bug (http://code.google.com/p/chromium/issues/detail?id=15461) - if (isWebKit && key === 'stroke-width' && value === 0) { - value = 0.000001; + // In Chrome/Win < 6 as well as Batik, the stroke attribute can't be set when the stroke- + // width is 0. #1369 + if (key === 'stroke-width' || key === 'stroke') { + wrapper[key] = value; + // Only apply the stroke attribute if the stroke width is defined and larger than 0 + if (wrapper.stroke && wrapper['stroke-width']) { + attr(element, 'stroke', wrapper.stroke); + attr(element, 'stroke-width', wrapper['stroke-width']); + wrapper.hasStroke = true; + } else if (key === 'stroke-width' && value === 0 && wrapper.hasStroke) { + element.removeAttribute('stroke'); + wrapper.hasStroke = false; + } + skipAttr = true; } // symbols @@ -1988,14 +2237,12 @@ // Record for animation and quick access without polling the DOM wrapper[key] = value; - // Update transform - if (doTransform) { - wrapper.updateTransform(); - } - - + if (key === 'text') { - // only one node allowed + // Delete bBox memo when the text changes + if (value !== wrapper.textStr) { + delete wrapper.bBox; + } wrapper.textStr = value; if (wrapper.added) { renderer.buildText(wrapper); @@ -2008,30 +2255,35 @@ } - } - - // Workaround for our #732, WebKit's issue https://bugs.webkit.org/show_bug.cgi?id=78385 - // TODO: If the WebKit team fix this bug before the final release of Chrome 18, remove the workaround. - if (isWebKit && /Chrome\/(18|19)/.test(userAgent)) { - if (nodeName === 'text' && (hash.x !== UNDEFINED || hash.y !== UNDEFINED)) { - var parent = element.parentNode, - next = element.nextSibling; - - if (parent) { - parent.removeChild(element); - if (next) { - parent.insertBefore(element, next); - } else { - parent.appendChild(element); - } - } + // Update transform. Do this outside the loop to prevent redundant updating for batch setting + // of attributes. + if (doTransform) { + wrapper.updateTransform(); } + } - // End of workaround for #732 return ret; }, + + /** + * Add a class name to an element + */ + addClass: function (className) { + attr(this.element, 'class', attr(this.element, 'class') + ' ' + className); + return this; + }, + /* hasClass and removeClass are not (yet) needed + hasClass: function (className) { + return attr(this.element, 'class').indexOf(className) !== -1; + }, + removeClass: function (className) { + attr(this.element, 'class', attr(this.element, 'class').replace(className, '')); + return this; + }, + */ + /** * If one of the symbol size affecting parameters are changed, * check all the others only once for each call to an element's @@ -2046,7 +2298,13 @@ }); wrapper.attr({ - d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper) + d: wrapper.renderer.symbols[wrapper.symbolName]( + wrapper.x, + wrapper.y, + wrapper.width, + wrapper.height, + wrapper + ) }); }, @@ -2055,7 +2313,7 @@ * @param {String} id */ clip: function (clipRect) { - return this.attr('clip-path', 'url(' + this.renderer.url + '#' + clipRect.id + ')'); + return this.attr('clip-path', clipRect ? 'url(' + this.renderer.url + '#' + clipRect.id + ')' : NONE); }, /** @@ -2102,7 +2360,7 @@ /*jslint unparam: true*//* allow unused param a in the regexp function below */ var elemWrapper = this, elem = elemWrapper.element, - textWidth = styles && styles.width && elem.nodeName === 'text', + textWidth = styles && styles.width && elem.nodeName.toLowerCase() === 'text', n, serializedCss = '', hyphenate = function (a, b) { return '-' + b.toLowerCase(); }; @@ -2121,7 +2379,13 @@ // store object elemWrapper.styles = styles; - + + + // Don't handle line wrap on canvas + if (useCanVG && textWidth) { + delete styles.width; + } + // serialize and set style attribute if (isIE && !hasSVG) { // legacy IE doesn't support setting style attribute if (textWidth) { @@ -2132,9 +2396,7 @@ for (n in styles) { serializedCss += n.replace(/([A-Z])/g, hyphenate) + ':' + styles[n] + ';'; } - elemWrapper.attr({ - style: serializedCss - }); + attr(elem, 'style', serializedCss); // #1881 } @@ -2152,17 +2414,15 @@ * @param {Function} handler */ on: function (eventType, handler) { - var fn = handler; // touch if (hasTouch && eventType === 'click') { - eventType = 'touchstart'; - fn = function (e) { + this.element.ontouchstart = function (e) { e.preventDefault(); handler(); }; } // simplest possible event model for internal use - this.element['on' + eventType] = fn; + this.element['on' + eventType] = handler; return this; }, @@ -2229,14 +2489,14 @@ * @return {Object} A hash containing values for x, y, width and height */ - htmlGetBBox: function (refresh) { + htmlGetBBox: function () { var wrapper = this, element = wrapper.element, bBox = wrapper.bBox; // faking getBBox in exported SVG in legacy IE - if (!bBox || refresh) { - // faking getBBox in exported SVG in legacy IE + if (!bBox) { + // faking getBBox in exported SVG in legacy IE (is this a duplicate of the fix for #1079?) if (element.nodeName === 'text') { element.style.position = ABSOLUTE; } @@ -2276,19 +2536,17 @@ shadows = wrapper.shadows; // apply translate - if (translateX || translateY) { - css(elem, { - marginLeft: translateX, - marginTop: translateY - }); - if (shadows) { // used in labels/tooltip - each(shadows, function (shadow) { - css(shadow, { - marginLeft: translateX + 1, - marginTop: translateY + 1 - }); + css(elem, { + marginLeft: translateX, + marginTop: translateY + }); + if (shadows) { // used in labels/tooltip + each(shadows, function (shadow) { + css(shadow, { + marginLeft: translateX + 1, + marginTop: translateY + 1 }); - } + }); } // apply inversion @@ -2310,32 +2568,40 @@ textWidth = pInt(wrapper.textWidth), xCorr = wrapper.xCorr || 0, yCorr = wrapper.yCorr || 0, - currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','); + currentTextTransform = [rotation, align, elem.innerHTML, wrapper.textWidth].join(','), + rotationStyle = {}, + cssTransformKey; if (currentTextTransform !== wrapper.cTT) { // do the calculations and DOM access only if properties changed if (defined(rotation)) { - radians = rotation * deg2rad; // deg to rad - costheta = mathCos(radians); - sintheta = mathSin(radians); - - // Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented - // but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+ - // has support for CSS3 transform. The getBBox method also needs to be updated - // to compensate for the rotation, like it currently does for SVG. - // Test case: http://highcharts.com/tests/?file=text-rotation - css(elem, { - filter: rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta, - ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta, - ', sizingMethod=\'auto expand\')'].join('') : NONE - }); + + if (renderer.isSVG) { // #916 + cssTransformKey = isIE ? '-ms-transform' : isWebKit ? '-webkit-transform' : isFirefox ? 'MozTransform' : isOpera ? '-o-transform' : ''; + rotationStyle[cssTransformKey] = rotationStyle.transform = 'rotate(' + rotation + 'deg)'; + + } else { + radians = rotation * deg2rad; // deg to rad + costheta = mathCos(radians); + sintheta = mathSin(radians); + + // Adjust for alignment and rotation. Rotation of useHTML content is not yet implemented + // but it can probably be implemented for Firefox 3.5+ on user request. FF3.5+ + // has support for CSS3 transform. The getBBox method also needs to be updated + // to compensate for the rotation, like it currently does for SVG. + // Test case: http://highcharts.com/tests/?file=text-rotation + rotationStyle.filter = rotation ? ['progid:DXImageTransform.Microsoft.Matrix(M11=', costheta, + ', M12=', -sintheta, ', M21=', sintheta, ', M22=', costheta, + ', sizingMethod=\'auto expand\')'].join('') : NONE; + } + css(elem, rotationStyle); } width = pick(wrapper.elemWidth, elem.offsetWidth); height = pick(wrapper.elemHeight, elem.offsetHeight); // update textWidth - if (width > textWidth && /[ \-]/.test(elem.innerText)) { // #983 + if (width > textWidth && /[ \-]/.test(elem.textContent || elem.innerText)) { // #983, #1254 css(elem, { width: textWidth + PX, display: 'block', @@ -2375,6 +2641,11 @@ left: (x + xCorr) + PX, top: (y + yCorr) + PX }); + + // force reflow in webkit to apply the left and top on useHTML element (#1249) + if (isWebKit) { + height = elem.offsetHeight; // assigned to height for JSLint purpose + } // record current text transform wrapper.cTT = currentTextTransform; @@ -2389,9 +2660,11 @@ var wrapper = this, translateX = wrapper.translateX || 0, translateY = wrapper.translateY || 0, + scaleX = wrapper.scaleX, + scaleY = wrapper.scaleY, inverted = wrapper.inverted, rotation = wrapper.rotation, - transform = []; + transform; // flipping affects translate as adjustment for flipping around the group's axis if (inverted) { @@ -2399,10 +2672,9 @@ translateY += wrapper.attr('height'); } - // apply translate - if (translateX || translateY) { - transform.push('translate(' + translateX + ',' + translateY + ')'); - } + // Apply translate. Nearly all transformed elements have translation, so instead + // of checking for translate = 0, do it always (#1767, #1846). + transform = ['translate(' + translateX + ',' + translateY + ')']; // apply rotation if (inverted) { @@ -2411,6 +2683,11 @@ transform.push('rotate(' + rotation + ' ' + (wrapper.x || 0) + ' ' + (wrapper.y || 0) + ')'); } + // apply scale + if (defined(scaleX) || defined(scaleY)) { + transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')'); + } + if (transform.length) { attr(wrapper.element, 'transform', transform.join(' ')); } @@ -2431,103 +2708,136 @@ * * @param {Object} alignOptions * @param {Boolean} alignByTranslate - * @param {Object} box The box to align to, needs a width and height + * @param {String[Object} box The box to align to, needs a width and height. When the + * box is a string, it refers to an object in the Renderer. For example, when + * box is 'spacingBox', it refers to Renderer.spacingBox which holds width, height + * x and y properties. * */ align: function (alignOptions, alignByTranslate, box) { - var elemWrapper = this; + var align, + vAlign, + x, + y, + attribs = {}, + alignTo, + renderer = this.renderer, + alignedObjects = renderer.alignedObjects; - if (!alignOptions) { // called on resize - alignOptions = elemWrapper.alignOptions; - alignByTranslate = elemWrapper.alignByTranslate; - } else { // first call on instanciate - elemWrapper.alignOptions = alignOptions; - elemWrapper.alignByTranslate = alignByTranslate; - if (!box) { // boxes other than renderer handle this internally - elemWrapper.renderer.alignedObjects.push(elemWrapper); + // First call on instanciate + if (alignOptions) { + this.alignOptions = alignOptions; + this.alignByTranslate = alignByTranslate; + if (!box || isString(box)) { // boxes other than renderer handle this internally + this.alignTo = alignTo = box || 'renderer'; + erase(alignedObjects, this); // prevent duplicates, like legendGroup after resize + alignedObjects.push(this); + box = null; // reassign it below } + + // When called on resize, no arguments are supplied + } else { + alignOptions = this.alignOptions; + alignByTranslate = this.alignByTranslate; + alignTo = this.alignTo; } - box = pick(box, elemWrapper.renderer); - - var align = alignOptions.align, - vAlign = alignOptions.verticalAlign, - x = (box.x || 0) + (alignOptions.x || 0), // default: left align - y = (box.y || 0) + (alignOptions.y || 0), // default: top align - attribs = {}; + box = pick(box, renderer[alignTo], renderer); + // Assign variables + align = alignOptions.align; + vAlign = alignOptions.verticalAlign; + x = (box.x || 0) + (alignOptions.x || 0); // default: left align + y = (box.y || 0) + (alignOptions.y || 0); // default: top align - // align - if (/^(right|center)$/.test(align)) { + // Align + if (align === 'right' || align === 'center') { x += (box.width - (alignOptions.width || 0)) / { right: 1, center: 2 }[align]; } attribs[alignByTranslate ? 'translateX' : 'x'] = mathRound(x); - // vertical align - if (/^(bottom|middle)$/.test(vAlign)) { + // Vertical align + if (vAlign === 'bottom' || vAlign === 'middle') { y += (box.height - (alignOptions.height || 0)) / ({ bottom: 1, middle: 2 }[vAlign] || 1); } attribs[alignByTranslate ? 'translateY' : 'y'] = mathRound(y); - // animate only if already placed - elemWrapper[elemWrapper.placed ? 'animate' : 'attr'](attribs); - elemWrapper.placed = true; - elemWrapper.alignAttr = attribs; + // Animate only if already placed + this[this.placed ? 'animate' : 'attr'](attribs); + this.placed = true; + this.alignAttr = attribs; - return elemWrapper; + return this; }, /** * Get the bounding box (width, height, x and y) for the element */ - getBBox: function (refresh) { + getBBox: function () { var wrapper = this, - bBox, + bBox = wrapper.bBox, + renderer = wrapper.renderer, width, height, rotation = wrapper.rotation, element = wrapper.element, + styles = wrapper.styles, rad = rotation * deg2rad; - - // SVG elements - if (element.namespaceURI === SVG_NS || wrapper.renderer.forExport) { - try { // Fails in Firefox if the container has display: none. - - bBox = element.getBBox ? - // SVG: use extend because IE9 is not allowed to change width and height in case - // of rotation (below) - extend({}, element.getBBox()) : - // Canvas renderer and legacy IE in export mode - { - width: element.offsetWidth, - height: element.offsetHeight - }; - } catch (e) {} - // If the bBox is not set, the try-catch block above failed. The other condition - // is for Opera that returns a width of -Infinity on hidden elements. - if (!bBox || bBox.width < 0) { - bBox = { width: 0, height: 0 }; + if (!bBox) { + // SVG elements + if (element.namespaceURI === SVG_NS || renderer.forExport) { + try { // Fails in Firefox if the container has display: none. + + bBox = element.getBBox ? + // SVG: use extend because IE9 is not allowed to change width and height in case + // of rotation (below) + extend({}, element.getBBox()) : + // Canvas renderer and legacy IE in export mode + { + width: element.offsetWidth, + height: element.offsetHeight + }; + } catch (e) {} + + // If the bBox is not set, the try-catch block above failed. The other condition + // is for Opera that returns a width of -Infinity on hidden elements. + if (!bBox || bBox.width < 0) { + bBox = { width: 0, height: 0 }; + } + + + // VML Renderer or useHTML within SVG + } else { + + bBox = wrapper.htmlGetBBox(); + } - width = bBox.width; - height = bBox.height; - - // adjust for rotated text - if (rotation) { - bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad)); - bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad)); + // True SVG elements as well as HTML elements in modern browsers using the .useHTML option + // need to compensated for rotation + if (renderer.isSVG) { + width = bBox.width; + height = bBox.height; + + // Workaround for wrong bounding box in IE9 and IE10 (#1101, #1505, #1669) + if (isIE && styles && styles.fontSize === '11px' && height.toPrecision(3) === '22.7') { + bBox.height = height = 14; + } + + // Adjust for rotated text + if (rotation) { + bBox.width = mathAbs(height * mathSin(rad)) + mathAbs(width * mathCos(rad)); + bBox.height = mathAbs(height * mathCos(rad)) + mathAbs(width * mathSin(rad)); + } } - - // VML Renderer or useHTML within SVG - } else { - bBox = wrapper.htmlGetBBox(refresh); + + wrapper.bBox = bBox; } - return bBox; }, @@ -2544,7 +2854,19 @@ hide: function () { return this.attr({ visibility: HIDDEN }); }, - + + fadeOut: function (duration) { + var elemWrapper = this; + elemWrapper.animate({ + opacity: 0 + }, { + duration: duration || 150, + complete: function () { + elemWrapper.hide(); + } + }); + }, + /** * Add the element * @param {Object|Undefined} parent Can be an element, an element wrapper or undefined @@ -2562,6 +2884,10 @@ otherZIndex, i, inserted; + + if (parent) { + this.parentGroup = parent; + } // mark as inverted this.parentInverted = parent && parent.inverted; @@ -2628,12 +2954,11 @@ var wrapper = this, element = wrapper.element || {}, shadows = wrapper.shadows, - box = wrapper.box, key, i; // remove events - element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = null; + element.onclick = element.onmouseout = element.onmouseover = element.onmousemove = element.point = null; stop(wrapper); // stop running animations if (wrapper.clipPath) { @@ -2658,13 +2983,10 @@ }); } - // destroy label box - if (box) { - box.destroy(); - } - // remove from alignObjects - erase(wrapper.renderer.alignedObjects, wrapper); + if (wrapper.alignTo) { + erase(wrapper.renderer.alignedObjects, wrapper); + } for (key in wrapper) { delete wrapper[key]; @@ -2674,41 +2996,35 @@ }, /** - * Empty a group element - */ - empty: function () { - var element = this.element, - childNodes = element.childNodes, - i = childNodes.length; - - while (i--) { - element.removeChild(childNodes[i]); - } - }, - - /** * Add a shadow to the element. Must be done after the element is added to the DOM - * @param {Boolean} apply + * @param {Boolean|Object} shadowOptions */ - shadow: function (apply, group, cutOff) { + shadow: function (shadowOptions, group, cutOff) { var shadows = [], i, shadow, element = this.element, strokeWidth, + shadowWidth, + shadowElementOpacity, // compensate for inverted plot area - transform = this.parentInverted ? '(-1,-1)' : '(1,1)'; + transform; - if (apply) { - for (i = 1; i <= 3; i++) { + if (shadowOptions) { + shadowWidth = pick(shadowOptions.width, 3); + shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth; + transform = this.parentInverted ? + '(-1,-1)' : + '(' + pick(shadowOptions.offsetX, 1) + ', ' + pick(shadowOptions.offsetY, 1) + ')'; + for (i = 1; i <= shadowWidth; i++) { shadow = element.cloneNode(0); - strokeWidth = 7 - 2 * i; + strokeWidth = (shadowWidth * 2) + 1 - (2 * i); attr(shadow, { 'isShadow': 'true', - 'stroke': 'rgb(0, 0, 0)', - 'stroke-opacity': 0.05 * i, + 'stroke': shadowOptions.color || 'black', + 'stroke-opacity': shadowElementOpacity * i, 'stroke-width': strokeWidth, 'transform': 'translate' + transform, 'fill': NONE @@ -2754,7 +3070,8 @@ init: function (container, width, height, forExport) { var renderer = this, loc = location, - boxWrapper; + boxWrapper, + desc; boxWrapper = renderer.createElement('svg') .attr({ @@ -2768,8 +3085,20 @@ renderer.box = boxWrapper.element; renderer.boxWrapper = boxWrapper; renderer.alignedObjects = []; - renderer.url = isIE ? '' : loc.href.replace(/#.*?$/, '') - .replace(/([\('\)])/g, '\\$1'); // Page url used for internal references. #24, #672. + + // Page url used for internal references. #24, #672, #1070 + renderer.url = (isFirefox || isWebKit) && doc.getElementsByTagName('base').length ? + loc.href + .replace(/#.*?$/, '') // remove the hash + .replace(/([\('\)])/g, '\\$1') // escape parantheses and quotes + .replace(/ /g, '%20') : // replace spaces (needed for Safari only) + ''; + + // Add description + desc = this.createElement('desc').add(); + desc.element.appendChild(doc.createTextNode('Created with ' + PRODUCT + ' ' + VERSION)); + + renderer.defs = this.createElement('defs').add(); renderer.forExport = forExport; renderer.gradients = {}; // Object where gradient SvgElements are stored @@ -2864,6 +3193,8 @@ */ buildText: function (wrapper) { var textNode = wrapper.element, + renderer = this, + forExport = renderer.forExport, lines = pick(wrapper.textStr, '').toString() .replace(/<(b|strong)>/g, '') .replace(/<(i|em)>/g, '') @@ -2875,20 +3206,11 @@ hrefRegex = /href="([^"]+)"/, parentX = attr(textNode, 'x'), textStyles = wrapper.styles, - width = textStyles && pInt(textStyles.width), + width = textStyles && textStyles.width && pInt(textStyles.width), textLineHeight = textStyles && textStyles.lineHeight, - lastLine, - GET_COMPUTED_STYLE = 'getComputedStyle', - i = childNodes.length, - linePositions = []; - - // Needed in IE9 because it doesn't report tspan's offsetHeight (#893) - function getLineHeightByBBox(lineNo) { - linePositions[lineNo] = textNode.getBBox().height; - return mathRound(linePositions[lineNo] - (linePositions[lineNo - 1] || 0)); - } + i = childNodes.length; - // remove old text + /// remove old text while (i--) { textNode.removeChild(childNodes[i]); } @@ -2904,7 +3226,7 @@ // build the lines each(lines, function (line, lineNo) { - var spans, spanNo = 0, lineHeight; + var spans, spanNo = 0; line = line.replace(//g, '|||'); spans = line.split('|||'); @@ -2912,15 +3234,13 @@ each(spans, function (span) { if (span !== '' || spans.length === 1) { var attributes = {}, - tspan = doc.createElementNS(SVG_NS, 'tspan'); + tspan = doc.createElementNS(SVG_NS, 'tspan'), + spanStyle; // #390 if (styleRegex.test(span)) { - attr( - tspan, - 'style', - span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2') - ); + spanStyle = span.match(styleRegex)[1].replace(/(;| |^)color([ :])/, '$1fill$2'); + attr(tspan, 'style', spanStyle); } - if (hrefRegex.test(span)) { + if (hrefRegex.test(span) && !forExport) { // Not for export - #1529 attr(tspan, 'onclick', 'location.href=\"' + span.match(hrefRegex)[1] + '\"'); css(tspan, { cursor: 'pointer' }); } @@ -2929,64 +3249,56 @@ .replace(/</g, '<') .replace(/>/g, '>'); - // issue #38 workaround. - /*if (reverse) { - arr = []; - i = span.length; - while (i--) { - arr.push(span.charAt(i)); - } - span = arr.join(''); - }*/ - // add the text node tspan.appendChild(doc.createTextNode(span)); if (!spanNo) { // first span in a line, align it to the left attributes.x = parentX; } else { - // Firefox ignores spaces at the front or end of the tspan - attributes.dx = 3; // space + attributes.dx = 0; // #16 } - // first span on subsequent line, add the line height - if (!spanNo) { - if (lineNo) { - - // allow getting the right offset height in exporting in IE - if (!hasSVG && wrapper.renderer.forExport) { - css(tspan, { display: 'block' }); - } + // add attributes + attr(tspan, attributes); - // Webkit and opera sometimes return 'normal' as the line height. In that - // case, webkit uses offsetHeight, while Opera falls back to 18 - lineHeight = win[GET_COMPUTED_STYLE] && - pInt(win[GET_COMPUTED_STYLE](lastLine, null).getPropertyValue('line-height')); + // first span on subsequent line, add the line height + if (!spanNo && lineNo) { - if (!lineHeight || isNaN(lineHeight)) { - lineHeight = textLineHeight || lastLine.offsetHeight || getLineHeightByBBox(lineNo) || 18; - } - attr(tspan, 'dy', lineHeight); + // allow getting the right offset height in exporting in IE + if (!hasSVG && forExport) { + css(tspan, { display: 'block' }); } - lastLine = tspan; // record for use in next line - } - // add attributes - attr(tspan, attributes); + // Set the line height based on the font size of either + // the text element or the tspan element + attr( + tspan, + 'dy', + textLineHeight || renderer.fontMetrics( + /px$/.test(tspan.style.fontSize) ? + tspan.style.fontSize : + textStyles.fontSize + ).h, + // Safari 6.0.2 - too optimized for its own good (#1539) + // TODO: revisit this with future versions of Safari + isWebKit && tspan.offsetHeight + ); + } - // append it + // Append it textNode.appendChild(tspan); spanNo++; // check width and apply soft breaks if (width) { - var words = span.replace(/-/g, '- ').split(' '), + var words = span.replace(/([^\^])-/g, '$1- ').split(' '), // #1273 tooLong, actualWidth, rest = []; while (words.length || rest.length) { + delete wrapper.bBox; // delete cache actualWidth = wrapper.getBBox().width; tooLong = actualWidth > width; if (!tooLong || words.length === 1) { // new line needed @@ -2998,6 +3310,9 @@ dy: textLineHeight || 16, x: parentX }); + if (spanStyle) { // #390 + attr(tspan, 'style', spanStyle); + } textNode.appendChild(tspan); if (actualWidth > width) { // a single word is pressing it out @@ -3029,7 +3344,7 @@ * @param {Object} pressedState */ button: function (text, x, y, callback, normalState, hoverState, pressedState) { - var label = this.label(text, x, y), + var label = this.label(text, x, y, null, null, null, null, null, 'button'), curState = 0, stateOptions, stateStyle, @@ -3039,55 +3354,51 @@ STYLE = 'style', verticalGradient = { x1: 0, y1: 0, x2: 0, y2: 1 }; - // prepare the attributes - /*jslint white: true*/ - normalState = merge(hash( - STROKE_WIDTH, 1, - STROKE, '#999', - FILL, hash( - LINEAR_GRADIENT, verticalGradient, - STOPS, [ - [0, '#FFF'], - [1, '#DDD'] + // Normal state - prepare the attributes + normalState = merge({ + 'stroke-width': 1, + stroke: '#CCCCCC', + fill: { + linearGradient: verticalGradient, + stops: [ + [0, '#FEFEFE'], + [1, '#F6F6F6'] ] - ), - 'r', 3, - 'padding', 3, - STYLE, hash( - 'color', 'black' - ) - ), normalState); - /*jslint white: false*/ + }, + r: 2, + padding: 5, + style: { + color: 'black' + } + }, normalState); normalStyle = normalState[STYLE]; delete normalState[STYLE]; - /*jslint white: true*/ - hoverState = merge(normalState, hash( - STROKE, '#68A', - FILL, hash( - LINEAR_GRADIENT, verticalGradient, - STOPS, [ + // Hover state + hoverState = merge(normalState, { + stroke: '#68A', + fill: { + linearGradient: verticalGradient, + stops: [ [0, '#FFF'], [1, '#ACF'] ] - ) - ), hoverState); - /*jslint white: false*/ + } + }, hoverState); hoverStyle = hoverState[STYLE]; delete hoverState[STYLE]; - /*jslint white: true*/ - pressedState = merge(normalState, hash( - STROKE, '#68A', - FILL, hash( - LINEAR_GRADIENT, verticalGradient, - STOPS, [ + // Pressed state + pressedState = merge(normalState, { + stroke: '#68A', + fill: { + linearGradient: verticalGradient, + stops: [ [0, '#9BD'], [1, '#CDF'] ] - ) - ), pressedState); - /*jslint white: false*/ + } + }, pressedState); pressedStyle = pressedState[STYLE]; delete pressedState[STYLE]; @@ -3131,7 +3442,8 @@ // points format: [M, 0, 0, L, 100, 0] // normalize to a crisp line if (points[1] === points[4]) { - points[1] = points[4] = mathRound(points[1]) + (width % 2 / 2); + // Substract due to #1129. Now bottom and left axis gridlines behave the same. + points[1] = points[4] = mathRound(points[1]) - (width % 2 / 2); } if (points[2] === points[5]) { points[2] = points[5] = mathRound(points[2]) + (width % 2 / 2); @@ -3327,6 +3639,7 @@ options ), + imageElement, imageRegex = /^url\((.*?)\)$/, imageSrc, imageSize, @@ -3353,41 +3666,44 @@ // On image load, set the size and position centerImage = function (img, size) { - img.attr({ - width: size[0], - height: size[1] - }); + if (img.element) { // it may be destroyed in the meantime (#1390) + img.attr({ + width: size[0], + height: size[1] + }); - if (!img.alignByTranslate) { // #185 - img.translate( - -mathRound(size[0] / 2), - -mathRound(size[1] / 2) - ); + if (!img.alignByTranslate) { // #185 + img.translate( + mathRound((width - size[0]) / 2), // #1378 + mathRound((height - size[1]) / 2) + ); + } } }; imageSrc = symbol.match(imageRegex)[1]; imageSize = symbolSizes[imageSrc]; - // create the image synchronously, add attribs async + // Ireate the image synchronously, add attribs async obj = this.image(imageSrc) .attr({ x: x, y: y }); + obj.isImg = true; if (imageSize) { centerImage(obj, imageSize); } else { - // initialize image to be 0 size so export will still function if there's no cached sizes + // Initialize image to be 0 size so export will still function if there's no cached sizes. + // obj.attr({ width: 0, height: 0 }); - // create a dummy JavaScript image to get the width and height - createElement('img', { + // Create a dummy JavaScript image to get the width and height. Due to a bug in IE < 8, + // the created element must be assigned to a variable in order to load (#292). + imageElement = createElement('img', { onload: function () { - var img = this; - - centerImage(obj, symbolSizes[imageSrc] = [img.width, img.height]); + centerImage(obj, symbolSizes[imageSrc] = [this.width, this.height]); }, src: imageSrc }); @@ -3450,7 +3766,7 @@ 'arc': function (x, y, w, h, options) { var start = options.start, radius = options.r || w || h, - end = options.end - 0.000001, // to prevent cos and sin of start and end from becoming equal on 360 arcs + end = options.end - 0.001, // to prevent cos and sin of start and end from becoming equal on 360 arcs (related: #1561) innerRadius = options.innerR, open = options.open, cosStart = mathCos(start), @@ -3526,7 +3842,17 @@ var renderer = this, colorObject, regexRgba = /^rgba/, - gradName; + gradName, + gradAttr, + gradients, + gradientObject, + stops, + stopColor, + stopOpacity, + radialReference, + n, + id, + key = []; // Apply linear or radial gradients if (color && color.linearGradient) { @@ -3536,47 +3862,59 @@ } if (gradName) { - var gradAttr = color[gradName], - gradients = renderer.gradients, - gradientObject, - stopColor, - stopOpacity, - radialReference = elem.radialReference; + gradAttr = color[gradName]; + gradients = renderer.gradients; + stops = color.stops; + radialReference = elem.radialReference; + + // Keep < 2.2 kompatibility + if (isArray(gradAttr)) { + color[gradName] = gradAttr = { + x1: gradAttr[0], + y1: gradAttr[1], + x2: gradAttr[2], + y2: gradAttr[3], + gradientUnits: 'userSpaceOnUse' + }; + } + + // Correct the radial gradient for the radial reference system + if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) { + gradAttr = merge(gradAttr, { + cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2], + cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2], + r: gradAttr.r * radialReference[2], + gradientUnits: 'userSpaceOnUse' + }); + } + + // Build the unique key to detect whether we need to create a new element (#1282) + for (n in gradAttr) { + if (n !== 'id') { + key.push(n, gradAttr[n]); + } + } + for (n in stops) { + key.push(stops[n]); + } + key = key.join(','); // Check if a gradient object with the same config object is created within this renderer - if (!gradAttr.id || !gradients[gradAttr.id]) { + if (gradients[key]) { + id = gradients[key].id; - // Keep < 2.2 kompatibility - if (isArray(gradAttr)) { - color[gradName] = gradAttr = { - x1: gradAttr[0], - y1: gradAttr[1], - x2: gradAttr[2], - y2: gradAttr[3], - gradientUnits: 'userSpaceOnUse' - }; - } - - // Correct the radial gradient for the radial reference system - if (gradName === 'radialGradient' && radialReference && !defined(gradAttr.gradientUnits)) { - extend(gradAttr, { - cx: (radialReference[0] - radialReference[2] / 2) + gradAttr.cx * radialReference[2], - cy: (radialReference[1] - radialReference[2] / 2) + gradAttr.cy * radialReference[2], - r: gradAttr.r * radialReference[2], - gradientUnits: 'userSpaceOnUse' - }); - } + } else { // Set the id and create the element - gradAttr.id = PREFIX + idCounter++; - gradients[gradAttr.id] = gradientObject = renderer.createElement(gradName) + gradAttr.id = id = PREFIX + idCounter++; + gradients[key] = gradientObject = renderer.createElement(gradName) .attr(gradAttr) .add(renderer.defs); // The gradient needs to keep a list of stops to be able to destroy them gradientObject.stops = []; - each(color.stops, function (stop) { + each(stops, function (stop) { var stopObject; if (regexRgba.test(stop[1])) { colorObject = Color(stop[1]); @@ -3598,7 +3936,7 @@ } // Return the reference to the gradient object - return 'url(' + renderer.url + '#' + gradAttr.id + ')'; + return 'url(' + renderer.url + '#' + id + ')'; // Webkit and Batik can't show rgba. } else if (regexRgba.test(color)) { @@ -3630,6 +3968,7 @@ // declare variables var renderer = this, defaultChartStyle = defaultOptions.chart.style, + fakeSVG = useCanVG || (!hasSVG && renderer.forExport), wrapper; if (useHTML && !renderer.forExport) { @@ -3649,6 +3988,13 @@ fontFamily: defaultChartStyle.fontFamily, fontSize: defaultChartStyle.fontSize }); + + // Prevent wrapping from creating false offsetWidths in export in legacy IE (#1079, #1063) + if (fakeSVG) { + wrapper.css({ + position: ABSOLUTE + }); + } wrapper.x = x; wrapper.y = y; @@ -3673,6 +4019,9 @@ // Text setter attrSetters.text = function (value) { + if (value !== element.innerHTML) { + delete this.bBox; + } element.innerHTML = value; return false; }; @@ -3708,33 +4057,55 @@ wrapper.add = function (svgGroupWrapper) { var htmlGroup, - htmlGroupStyle, - container = renderer.box.parentNode; + container = renderer.box.parentNode, + parentGroup, + parents = []; // Create a mock group to hold the HTML elements if (svgGroupWrapper) { htmlGroup = svgGroupWrapper.div; if (!htmlGroup) { - htmlGroup = svgGroupWrapper.div = createElement(DIV, { - className: attr(svgGroupWrapper.element, 'class') - }, { - position: ABSOLUTE, - left: svgGroupWrapper.attr('translateX') + PX, - top: svgGroupWrapper.attr('translateY') + PX - }, container); - - // Ensure dynamic updating position - htmlGroupStyle = htmlGroup.style; - extend(svgGroupWrapper.attrSetters, { - translateX: function (value) { - htmlGroupStyle.left = value + PX; - }, - translateY: function (value) { - htmlGroupStyle.top = value + PX; - }, - visibility: function (value, key) { - htmlGroupStyle[key] = value; - } + + // Read the parent chain into an array and read from top down + parentGroup = svgGroupWrapper; + while (parentGroup) { + + parents.push(parentGroup); + + // Move up to the next parent group + parentGroup = parentGroup.parentGroup; + } + + // Ensure dynamically updating position when any parent is translated + each(parents.reverse(), function (parentGroup) { + var htmlGroupStyle; + + // Create a HTML div and append it to the parent div to emulate + // the SVG group structure + htmlGroup = parentGroup.div = parentGroup.div || createElement(DIV, { + className: attr(parentGroup.element, 'class') + }, { + position: ABSOLUTE, + left: (parentGroup.translateX || 0) + PX, + top: (parentGroup.translateY || 0) + PX + }, htmlGroup || container); // the top group is appended to container + + // Shortcut + htmlGroupStyle = htmlGroup.style; + + // Set listeners to update the HTML div's position whenever the SVG group + // position is changed + extend(parentGroup.attrSetters, { + translateX: function (value) { + htmlGroupStyle.left = value + PX; + }, + translateY: function (value) { + htmlGroupStyle.top = value + PX; + }, + visibility: function (value, key) { + htmlGroupStyle[key] = value; + } + }); }); } @@ -3794,12 +4165,13 @@ text = renderer.text('', 0, 0, useHTML) .attr({ zIndex: 1 - }) - .add(wrapper), + }), + //.add(wrapper), box, bBox, alignFactor = 0, padding = 3, + paddingLeft = 0, width, height, wrapperX, @@ -3807,7 +4179,8 @@ crispAdjust = 0, deferredAttr = {}, baselineOffset, - attrSetters = wrapper.attrSetters; + attrSetters = wrapper.attrSetters, + needsBox; /** * This function runs after the label is added to the DOM (when the bounding box is @@ -3815,34 +4188,40 @@ * box and reflect it in the border box. */ function updateBoxSize() { - var boxY, + var boxX, + boxY, style = text.element.style; bBox = (width === undefined || height === undefined || wrapper.styles.textAlign) && - text.getBBox(true); - wrapper.width = (width || bBox.width || 0) + 2 * padding; + text.getBBox(); + wrapper.width = (width || bBox.width || 0) + 2 * padding + paddingLeft; wrapper.height = (height || bBox.height || 0) + 2 * padding; // update the label-scoped y offset baselineOffset = padding + renderer.fontMetrics(style && style.fontSize).b; - - - // create the border box if it is not already present - if (!box) { - boxY = baseline ? -baselineOffset : 0; - - wrapper.box = box = shape ? - renderer.symbol(shape, -alignFactor * padding, boxY, wrapper.width, wrapper.height) : - renderer.rect(-alignFactor * padding, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]); - box.add(wrapper); + + if (needsBox) { + + // create the border box if it is not already present + if (!box) { + boxX = mathRound(-alignFactor * padding); + boxY = baseline ? -baselineOffset : 0; + + wrapper.box = box = shape ? + renderer.symbol(shape, boxX, boxY, wrapper.width, wrapper.height) : + renderer.rect(boxX, boxY, wrapper.width, wrapper.height, 0, deferredAttr[STROKE_WIDTH]); + box.add(wrapper); + } + + // apply the box attributes + if (!box.isImg) { // #1630 + box.attr(merge({ + width: wrapper.width, + height: wrapper.height + }, deferredAttr)); + } + deferredAttr = null; } - - // apply the box attributes - box.attr(merge({ - width: wrapper.width, - height: wrapper.height - }, deferredAttr)); - deferredAttr = null; } /** @@ -3851,7 +4230,7 @@ function updateTextPadding() { var styles = wrapper.styles, textAlign = styles && styles.textAlign, - x = padding * (1 - alignFactor), + x = paddingLeft + padding * (1 - alignFactor), y; // determin y based on the baseline @@ -3889,13 +4268,14 @@ } function getSizeAfterAdd() { + text.add(wrapper); wrapper.attr({ text: str, // alignment is available now x: x, y: y }); - if (defined(anchorX)) { + if (box && defined(anchorX)) { wrapper.attr({ anchorX: anchorX, anchorY: anchorY @@ -3922,14 +4302,21 @@ height = value; return false; }; - attrSetters.padding = function (value) { + attrSetters.padding = function (value) { if (defined(value) && value !== padding) { padding = value; updateTextPadding(); } - return false; }; + attrSetters.paddingLeft = function (value) { + if (defined(value) && value !== paddingLeft) { + paddingLeft = value; + updateTextPadding(); + } + return false; + }; + // change local variable and set attribue as well attrSetters.align = function (value) { @@ -3947,11 +4334,15 @@ // apply these to the box but not to the text attrSetters[STROKE_WIDTH] = function (value, key) { + needsBox = true; crispAdjust = value % 2 / 2; boxAttr(key, value); return false; }; attrSetters.stroke = attrSetters.fill = attrSetters.r = function (value, key) { + if (key === 'fill') { + needsBox = true; + } boxAttr(key, value); return false; }; @@ -3977,7 +4368,7 @@ }; attrSetters.y = function (value) { wrapperY = wrapper.y = mathRound(value); - wrapper.attr('translateY', value); + wrapper.attr('translateY', wrapperY); return false; }; @@ -3990,8 +4381,8 @@ css: function (styles) { if (styles) { var textStyles = {}; - styles = merge({}, styles); // create a copy to avoid altering the original object (#537) - each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width'], function (prop) { + styles = merge(styles); // create a copy to avoid altering the original object (#537) + each(['fontSize', 'fontWeight', 'fontFamily', 'color', 'lineHeight', 'width', 'textDecoration'], function (prop) { if (styles[prop] !== UNDEFINED) { textStyles[prop] = styles[prop]; delete styles[prop]; @@ -4005,13 +4396,20 @@ * Return the bounding box of the box, not the group */ getBBox: function () { - return box.getBBox(); + return { + width: bBox.width + 2 * padding, + height: bBox.height + 2 * padding, + x: bBox.x - padding, + y: bBox.y - padding + }; }, /** * Apply the shadow to the box */ shadow: function (b) { - box.shadow(b); + if (box) { + box.shadow(b); + } return wrapper; }, /** @@ -4025,11 +4423,16 @@ removeEvent(wrapper.element, 'mouseleave'); if (text) { - // Destroy the text element text = text.destroy(); } + if (box) { + box = box.destroy(); + } // Call base implementation to destroy the rest SVGElement.prototype.destroy.call(wrapper); + + // Release local pointers (#1298) + wrapper = renderer = updateBoxSize = updateTextPadding = boxAttr = getSizeAfterAdd = null; } }); } @@ -4052,13 +4455,13 @@ /** * @constructor */ -var VMLRenderer; +var VMLRenderer, VMLElement; if (!hasSVG && !useCanVG) { /** * The VML element wrapper. */ -var VMLElement = { +Highcharts.VMLElement = VMLElement = { /** * Initialize a new VML element wrapper. It builds the markup as a string @@ -4069,21 +4472,20 @@ init: function (renderer, nodeName) { var wrapper = this, markup = ['<', nodeName, ' filled="f" stroked="f"'], - style = ['position: ', ABSOLUTE, ';']; + style = ['position: ', ABSOLUTE, ';'], + isDiv = nodeName === DIV; // divs and shapes need size - if (nodeName === 'shape' || nodeName === DIV) { + if (nodeName === 'shape' || isDiv) { style.push('left:0;top:0;width:1px;height:1px;'); } - if (docMode8) { - style.push('visibility: ', nodeName === DIV ? HIDDEN : VISIBLE); - } - + style.push('visibility: ', isDiv ? HIDDEN : VISIBLE); + markup.push(' style="', style.join(''), '"/>'); // create element with default attributes and style if (nodeName) { - markup = nodeName === DIV || nodeName === 'span' || nodeName === 'img' ? + markup = isDiv || nodeName === 'span' || nodeName === 'img' ? markup.join('') : renderer.prepVML(markup); wrapper.element = createElement(markup); @@ -4115,11 +4517,6 @@ renderer.invertChild(element, parentNode); } - // issue #140 workaround - related to #61 and #74 - if (docMode8 && parentNode.gVis === HIDDEN) { - css(element, { visibility: HIDDEN }); - } - // append it parentNode.appendChild(element); @@ -4128,7 +4525,7 @@ if (wrapper.alignOnAdd && !wrapper.deferUpdateTransform) { wrapper.updateTransform(); } - + // fire an event for internal hooks fireEvent(wrapper, 'add'); @@ -4136,26 +4533,6 @@ }, /** - * In IE8 documentMode 8, we need to recursively set the visibility down in the DOM - * tree for nested groups. Related to #61, #586. - */ - toggleChildren: function (element, visibility) { - var childNodes = element.childNodes, - i = childNodes.length; - - while (i--) { - - // apply the visibility - css(childNodes[i], { visibility: visibility }); - - // we have a nested group, apply it to its children again - if (childNodes[i].nodeName === 'DIV') { - this.toggleChildren(childNodes[i], visibility); - } - } - }, - - /** * VML always uses htmlUpdateTransform */ updateTransform: SVGElement.prototype.htmlUpdateTransform, @@ -4203,7 +4580,7 @@ skipAttr = false; // check for a specific attribute setter - result = attrSetters[key] && attrSetters[key](value, key); + result = attrSetters[key] && attrSetters[key].call(wrapper, value, key); if (result !== false && value !== null) { // #620 @@ -4231,7 +4608,8 @@ // convert paths i = value.length; - var convertedPath = []; + var convertedPath = [], + clockwise; while (i--) { // Multiply by 10 to allow subpixel precision. @@ -4243,6 +4621,20 @@ convertedPath[i] = 'x'; } else { convertedPath[i] = value[i]; + + // When the start X and end X coordinates of an arc are too close, + // they are rounded to the same value above. In this case, substract 1 from the end X + // position. #760, #1371. + if (value.isArc && (value[i] === 'wa' || value[i] === 'at')) { + clockwise = value[i] === 'wa' ? 1 : -1; // #1642 + if (convertedPath[i + 5] === convertedPath[i + 7]) { + convertedPath[i + 7] -= clockwise; + } + // Start and end Y (#1410) + if (convertedPath[i + 6] === convertedPath[i + 8]) { + convertedPath[i + 8] -= clockwise; + } + } } } @@ -4258,54 +4650,67 @@ } skipAttr = true; - // directly mapped to css - } else if (key === 'zIndex' || key === 'visibility') { + // handle visibility + } else if (key === 'visibility') { - // workaround for #61 and #586 - if (docMode8 && key === 'visibility' && nodeName === 'DIV') { - element.gVis = value; - wrapper.toggleChildren(element, value); - if (value === VISIBLE) { // #74 - value = null; + // let the shadow follow the main element + if (shadows) { + i = shadows.length; + while (i--) { + shadows[i].style[key] = value; } } + + // Instead of toggling the visibility CSS property, move the div out of the viewport. + // This works around #61 and #586 + if (nodeName === 'DIV') { + value = value === HIDDEN ? '-999em' : 0; + + // In order to redraw, IE7 needs the div to be visible when tucked away + // outside the viewport. So the visibility is actually opposite of + // the expected value. This applies to the tooltip only. + if (!docMode8) { + elemStyle[key] = value ? VISIBLE : HIDDEN; + } + key = 'top'; + } + elemStyle[key] = value; + skipAttr = true; + + // directly mapped to css + } else if (key === 'zIndex') { if (value) { elemStyle[key] = value; } - - - skipAttr = true; - // width and height - } else if (key === 'width' || key === 'height') { + // x, y, width, height + } else if (inArray(key, ['x', 'y', 'width', 'height']) !== -1) { - value = mathMax(0, value); // don't set width or height below zero (#311) + wrapper[key] = value; // used in getter + + if (key === 'x' || key === 'y') { + key = { x: 'left', y: 'top' }[key]; + } else { + value = mathMax(0, value); // don't set width or height below zero (#311) + } - this[key] = value; // used in getter - // clipping rectangle special if (wrapper.updateClipping) { - wrapper[key] = value; + wrapper[key] = value; // the key is now 'left' or 'top' for 'x' and 'y' wrapper.updateClipping(); } else { // normal elemStyle[key] = value; } - skipAttr = true; - - // x and y - } else if (key === 'x' || key === 'y') { - - wrapper[key] = value; // used in getter - elemStyle[{ x: 'left', y: 'top' }[key]] = value; + skipAttr = true; // class name - } else if (key === 'class') { + } else if (key === 'class' && nodeName === 'DIV') { // IE8 Standards mode has problems retrieving the className - element.className = value; + element.className = value; // stroke } else if (key === 'stroke') { @@ -4337,17 +4742,27 @@ if (nodeName === 'SPAN') { // text color elemStyle.color = value; - } else { + } else if (nodeName !== 'IMG') { // #1336 element.filled = value !== NONE ? true : false; - value = renderer.color(value, element, key); + value = renderer.color(value, element, key, wrapper); key = 'fillcolor'; } + + // opacity: don't bother - animation is too slow and filters introduce artifacts + } else if (key === 'opacity') { + /*css(element, { + opacity: value + });*/ + skipAttr = true; // rotation on VML elements } else if (nodeName === 'shape' && key === 'rotation') { wrapper[key] = value; + // Correction for the 1x1 size of the shape container. Used in gauge needles. + element.style.left = -mathRound(mathSin(value * deg2rad) + 1) + PX; + element.style.top = mathRound(mathCos(value * deg2rad)) + PX; // translation for animation } else if (key === 'translateX' || key === 'translateY' || key === 'rotation') { @@ -4361,16 +4776,7 @@ this.bBox = null; element.innerHTML = value; skipAttr = true; - } - - // let the shadow follow the main element - if (shadows && key === 'visibility') { - i = shadows.length; - while (i--) { - shadows[i].style[key] = value; - } - } - + } if (!skipAttr) { @@ -4394,21 +4800,27 @@ */ clip: function (clipRect) { var wrapper = this, - clipMembers = clipRect.members, - element = wrapper.element, - parentNode = element.parentNode; + clipMembers, + cssRet; - clipMembers.push(wrapper); - wrapper.destroyClip = function () { - erase(clipMembers, wrapper); - }; - - // Issue #863 workaround - related to #140, #61, #74 - if (parentNode && parentNode.className === 'highcharts-tracker' && !docMode8) { - css(element, { visibility: HIDDEN }); + if (clipRect) { + clipMembers = clipRect.members; + erase(clipMembers, wrapper); // Ensure unique list of elements (#1258) + clipMembers.push(wrapper); + wrapper.destroyClip = function () { + erase(clipMembers, wrapper); + }; + cssRet = clipRect.getCSS(wrapper); + + } else { + if (wrapper.destroyClip) { + wrapper.destroyClip(); + } + cssRet = { clip: docMode8 ? 'inherit' : 'rect(auto)' }; // #1214 } - return wrapper.css(clipRect.getCSS(wrapper)); + return wrapper.css(cssRet); + }, /** @@ -4424,8 +4836,7 @@ safeRemoveChild: function (element) { // discardElement will detach the node from its parent before attaching it // to the garbage bin. Therefore it is important that the node is attached and have parent. - var parentNode = element.parentNode; - if (parentNode) { + if (element.parentNode) { discardElement(element); } }, @@ -4434,28 +4845,11 @@ * Extend element.destroy by removing it from the clip members array */ destroy: function () { - var wrapper = this; - - if (wrapper.destroyClip) { - wrapper.destroyClip(); + if (this.destroyClip) { + this.destroyClip(); } - return SVGElement.prototype.destroy.apply(wrapper); - }, - - /** - * Remove all child nodes of a group, except the v:group element - */ - empty: function () { - var element = this.element, - childNodes = element.childNodes, - i = childNodes.length, - node; - - while (i--) { - node = childNodes[i]; - node.parentNode.removeChild(node); - } + return SVGElement.prototype.destroy.apply(this); }, /** @@ -4491,9 +4885,9 @@ /** * Apply a drop shadow by copying elements and giving them different strokes - * @param {Boolean} apply + * @param {Boolean|Object} shadowOptions */ - shadow: function (apply, group, cutOff) { + shadow: function (shadowOptions, group, cutOff) { var shadows = [], i, element = this.element, @@ -4503,7 +4897,9 @@ markup, path = element.path, strokeWidth, - modifiedPath; + modifiedPath, + shadowWidth, + shadowElementOpacity; // some times empty paths are not strings if (path && typeof path.value !== 'string') { @@ -4511,24 +4907,26 @@ } modifiedPath = path; - if (apply) { + if (shadowOptions) { + shadowWidth = pick(shadowOptions.width, 3); + shadowElementOpacity = (shadowOptions.opacity || 0.15) / shadowWidth; for (i = 1; i <= 3; i++) { - strokeWidth = 7 - 2 * i; + strokeWidth = (shadowWidth * 2) + 1 - (2 * i); // Cut off shadows for stacked column items if (cutOff) { modifiedPath = this.cutOffPath(path.value, strokeWidth + 0.5); } - markup = ['']; shadow = createElement(renderer.prepVML(markup), null, { - left: pInt(elemStyle.left) + 1, - top: pInt(elemStyle.top) + 1 + left: pInt(elemStyle.left) + pick(shadowOptions.offsetX, 1), + top: pInt(elemStyle.top) + pick(shadowOptions.offsetY, 1) } ); if (cutOff) { @@ -4536,7 +4934,7 @@ } // apply the opacity - markup = ['']; + markup = ['']; createElement(renderer.prepVML(markup), null, null, shadow); @@ -4589,6 +4987,7 @@ // generate the containing box + renderer.isVML = true; renderer.box = box; renderer.boxWrapper = boxWrapper; @@ -4631,19 +5030,23 @@ clipRect: function (x, y, width, height) { // create a dummy element - var clipRect = this.createElement(); - + var clipRect = this.createElement(), + isObj = isObject(x); + // mimic a rectangle with its style object for automatic updating in attr return extend(clipRect, { members: [], - left: x, - top: y, - width: width, - height: height, + left: isObj ? x.x : x, + top: isObj ? x.y : y, + width: isObj ? x.width : width, + height: isObj ? x.height : height, getCSS: function (wrapper) { - var inverted = wrapper.inverted, + var element = wrapper.element, + nodeName = element.nodeName, + isShape = nodeName === 'shape', + inverted = wrapper.inverted, rect = this, - top = rect.top, + top = rect.top - (isShape ? element.offsetTop : 0), left = rect.left, right = left + rect.width, bottom = top + rect.height, @@ -4656,13 +5059,12 @@ }; // issue 74 workaround - if (!inverted && docMode8 && wrapper.element.nodeName !== 'IMG') { + if (!inverted && docMode8 && nodeName === 'DIV') { extend(ret, { width: right + PX, height: bottom + PX }); } - return ret; }, @@ -4683,8 +5085,9 @@ * * @param {Object} color The color or config object */ - color: function (color, elem, prop) { - var colorObject, + color: function (color, elem, prop, wrapper) { + var renderer = this, + colorObject, regexRgba = /^rgba/, markup, fillType, @@ -4707,7 +5110,6 @@ y1, x2, y2, - angle, opacity1, opacity2, color1, @@ -4716,7 +5118,14 @@ stops = color.stops, firstStop, lastStop, - colors = []; + colors = [], + addFillNode = function () { + // Add the fill subnode. When colors attribute is used, the meanings of opacity and o:opacity2 + // are reversed. + markup = ['']; + createElement(renderer.prepVML(markup), null, null, elem); + }; // Extend from 0 to 1 firstStop = stops[0]; @@ -4758,65 +5167,67 @@ } }); - // Handle linear gradient angle - if (fillType === 'gradient') { - x1 = gradient.x1 || gradient[0] || 0; - y1 = gradient.y1 || gradient[1] || 0; - x2 = gradient.x2 || gradient[2] || 0; - y2 = gradient.y2 || gradient[3] || 0; - angle = 90 - math.atan( - (y2 - y1) / // y vector - (x2 - x1) // x vector - ) * 180 / mathPI; - - // Radial (circular) gradient - } else { - // pie: http://jsfiddle.net/highcharts/66g8H/ - // reference: http://jsfiddle.net/highcharts/etznJ/ - // http://jsfiddle.net/highcharts/XRbCc/ - // http://jsfiddle.net/highcharts/F3fwR/ - // TODO: - // - correct for radialRefeence - // - check whether gradient stops are supported - // - add global option for gradient image (must relate to version) - var r = gradient.r, - size = r * 2, - cx = gradient.cx, - cy = gradient.cy; - //radialReference = elem.radialReference; - - //if (radialReference) { - // Try setting pixel size, or other way to adjust the gradient size to the bounding box - //} - fillAttr = 'src="http://code.highcharts.com/gfx/radial-gradient.png" ' + - 'size="' + size + ',' + size + '" ' + - 'origin="0.5,0.5" ' + - 'position="' + cx + ',' + cy + '" ' + - 'color2="' + color2 + '" '; - - // The fill element's color attribute is broken in IE8 standards mode, so we - // need to set the parent shape's fillcolor attribute instead. - ret = color1; - } - - - // Apply the gradient to fills only. if (prop === 'fill') { - // when colors attribute is used, the meanings of opacity and o:opacity2 - // are reversed. - markup = ['']; - createElement(this.prepVML(markup), null, null, elem); + // Handle linear gradient angle + if (fillType === 'gradient') { + x1 = gradient.x1 || gradient[0] || 0; + y1 = gradient.y1 || gradient[1] || 0; + x2 = gradient.x2 || gradient[2] || 0; + y2 = gradient.y2 || gradient[3] || 0; + fillAttr = 'angle="' + (90 - math.atan( + (y2 - y1) / // y vector + (x2 - x1) // x vector + ) * 180 / mathPI) + '"'; + + addFillNode(); + + // Radial (circular) gradient + } else { + + var r = gradient.r, + sizex = r * 2, + sizey = r * 2, + cx = gradient.cx, + cy = gradient.cy, + radialReference = elem.radialReference, + bBox, + applyRadialGradient = function () { + if (radialReference) { + bBox = wrapper.getBBox(); + cx += (radialReference[0] - bBox.x) / bBox.width - 0.5; + cy += (radialReference[1] - bBox.y) / bBox.height - 0.5; + sizex *= radialReference[2] / bBox.width; + sizey *= radialReference[2] / bBox.height; + } + fillAttr = 'src="' + defaultOptions.global.VMLRadialGradientURL + '" ' + + 'size="' + sizex + ',' + sizey + '" ' + + 'origin="0.5,0.5" ' + + 'position="' + cx + ',' + cy + '" ' + + 'color2="' + color2 + '" '; + + addFillNode(); + }; + + // Apply radial gradient + if (wrapper.added) { + applyRadialGradient(); + } else { + // We need to know the bounding box to get the size and position right + addEvent(wrapper, 'add', applyRadialGradient); + } + + // The fill element's color attribute is broken in IE8 standards mode, so we + // need to set the parent shape's fillcolor attribute instead. + ret = color1; + } // Gradients are not supported for VML stroke, return the first color. #722. } else { ret = stopColor; } - // if the color is an rgba color, split it and add a fill node // to hold the opacity component } else if (regexRgba.test(color) && elem.tagName !== 'IMG') { @@ -4830,9 +5241,10 @@ } else { - var strokeNodes = elem.getElementsByTagName(prop); - if (strokeNodes.length) { - strokeNodes[0].opacity = 1; + var propNodes = elem.getElementsByTagName(prop); // 'stroke' or 'fill' node + if (propNodes.length) { + propNodes[0].opacity = 1; + propNodes[0].type = 'solid'; } ret = color; } @@ -4899,7 +5311,14 @@ * @param {Number} r */ circle: function (x, y, r) { - return this.symbol('circle').attr({ x: x - r, y: y - r, width: 2 * r, height: 2 * r }); + var circle = this.symbol('circle'); + if (isObject(x)) { + r = x.r; + y = x.y; + x = x.x; + } + circle.isCircle = true; // Causes x and y to mean center (#1682) + return circle.attr({ x: x, y: y, width: 2 * r, height: 2 * r }); }, /** @@ -4937,9 +5356,9 @@ .attr({ src: src }); if (arguments.length > 1) { - obj.css({ - left: x, - top: y, + obj.attr({ + x: x, + y: y, width: width, height: height }); @@ -4990,23 +5409,15 @@ var start = options.start, end = options.end, radius = options.r || w || h, + innerRadius = options.innerR, cosStart = mathCos(start), sinStart = mathSin(start), cosEnd = mathCos(end), sinEnd = mathSin(end), - innerRadius = options.innerR, - circleCorrection = 0.08 / radius, // #760 - innerCorrection = (innerRadius && 0.1 / innerRadius) || 0, ret; if (end - start === 0) { // no angle, don't show it. return ['x']; - - } else if (2 * mathPI - end + start < circleCorrection) { // full circle - // empirical correction found by trying out the limits for different radii - cosEnd = -circleCorrection; - } else if (end - start < innerCorrection) { // issue #186, another mysterious VML arc problem - cosEnd = mathCos(start + innerCorrection); } ret = [ @@ -5021,11 +5432,12 @@ y + radius * sinEnd // end y ]; - if (options.open) { + if (options.open && !innerRadius) { ret.push( + 'e', M, - x - innerRadius, - y - innerRadius + x,// - innerRadius, + y// - innerRadius ); } @@ -5043,12 +5455,19 @@ 'e' // close ); + ret.isArc = true; return ret; }, // Add circle symbol path. This performs significantly faster than v:oval. - circle: function (x, y, w, h) { + circle: function (x, y, w, h, wrapper) { + // Center correction, #1682 + if (wrapper && wrapper.isCircle) { + x -= w / 2; + y -= h / 2; + } + // Return the path return [ 'wa', // clockwisearcto x, // left @@ -5133,7 +5552,7 @@ } } }; -VMLRenderer = function () { +Highcharts.VMLRenderer = VMLRenderer = function () { this.init.apply(this, arguments); }; VMLRenderer.prototype = merge(SVGRenderer.prototype, VMLRendererExtension); @@ -5162,8 +5581,9 @@ * When requested, the CanVGController downloads the rest of the source packaged * together with the canvg library. */ - CanVGRenderer = function () { - // Empty constructor + Highcharts.CanVGRenderer = CanVGRenderer = function () { + // Override the global SVG namespace to fake SVG/HTML that accepts CSS + SVG_NS = 'http://www.w3.org/1999/xhtml'; }; /** @@ -5205,6 +5625,8 @@ } }; }()); + + Renderer = CanVGRenderer; } // end CanVGRenderer /* **************************************************************************** @@ -5214,19 +5636,15 @@ *****************************************************************************/ /** - * General renderer - */ -Renderer = VMLRenderer || CanVGRenderer || SVGRenderer; -/** * The Tick class */ -function Tick(axis, pos, type) { +function Tick(axis, pos, type, noLabel) { this.axis = axis; this.pos = pos; this.type = type || ''; this.isNew = true; - if (!type) { + if (!type && !noLabel) { this.addLabel(); } } @@ -5242,20 +5660,23 @@ chart = axis.chart, horiz = axis.horiz, categories = axis.categories, + names = axis.series[0] && axis.series[0].names, pos = tick.pos, labelOptions = options.labels, str, tickPositions = axis.tickPositions, - width = (categories && horiz && categories.length && + width = (horiz && categories && !labelOptions.step && !labelOptions.staggerLines && !labelOptions.rotation && chart.plotWidth / tickPositions.length) || - (!horiz && chart.plotWidth / 2), + (!horiz && (chart.optionsMarginLeft || chart.plotWidth / 2)), // #1580 isFirst = pos === tickPositions[0], isLast = pos === tickPositions[tickPositions.length - 1], css, attr, - value = categories && defined(categories[pos]) ? categories[pos] : pos, + value = categories ? + pick(categories[pos], names && names[pos], pos) : + pos, label = tick.label, tickPositionInfo = tickPositions.info, dateTimeLabelFormat; @@ -5303,7 +5724,7 @@ .attr(attr) // without position absolute, IE export sometimes is wrong .css(css) - .add(axis.axisGroup) : + .add(axis.labelGroup) : null; // update @@ -5322,7 +5743,7 @@ var label = this.label, axis = this.axis; return label ? - ((this.labelBBox = label.getBBox(true)))[axis.horiz ? 'height' : 'width'] : + ((this.labelBBox = label.getBBox()))[axis.horiz ? 'height' : 'width'] : 0; }, @@ -5363,7 +5784,7 @@ plotLeft = chart.plotLeft, plotRight = plotLeft + axis.len, neighbour = axis.ticks[tickPositions[index + (isFirst ? 1 : -1)]], - neighbourEdge = neighbour && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1]; + neighbourEdge = neighbour && neighbour.label.xy && neighbour.label.xy.x + neighbour.getLabelSides()[isFirst ? 0 : 1]; if ((isFirst && !reversed) || (isLast && reversed)) { // Is the label spilling out to the left of the plot area? @@ -5469,7 +5890,7 @@ * @param index {Number} * @param old {Boolean} Use old coordinates to prepare an animation into new position */ - render: function (index, old) { + render: function (index, old, opacity) { var tick = this, axis = tick.axis, options = axis.options, @@ -5496,15 +5917,18 @@ step = labelOptions.step, attribs, show = true, - tickmarkOffset = (options.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0, + tickmarkOffset = axis.tickmarkOffset, xy = tick.getPosition(horiz, pos, tickmarkOffset, old), x = xy.x, y = xy.y, + reverseCrisp = ((horiz && x === axis.pos) || (!horiz && y === axis.pos + axis.len)) ? -1 : 1, // #1480 staggerLines = axis.staggerLines; + + this.isActive = true; // create the grid line if (gridLineWidth) { - gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLineWidth, old); + gridLinePath = axis.getPlotLinePath(pos + tickmarkOffset, gridLineWidth * reverseCrisp, old, true); if (gridLine === UNDEFINED) { attribs = { @@ -5517,6 +5941,9 @@ if (!type) { attribs.zIndex = 1; } + if (old) { + attribs.opacity = 0; + } tick.gridLine = gridLine = gridLineWidth ? renderer.path(gridLinePath) @@ -5528,13 +5955,14 @@ // by another call, therefore do not do any animations this time if (!old && gridLine && gridLinePath) { gridLine[tick.isNew ? 'attr' : 'animate']({ - d: gridLinePath + d: gridLinePath, + opacity: opacity }); } } // create the tick mark - if (tickWidth) { + if (tickWidth && tickLength) { // negate the length if (tickPosition === 'inside') { @@ -5544,18 +5972,20 @@ tickLength = -tickLength; } - markPath = tick.getMarkPath(x, y, tickLength, tickWidth, horiz, renderer); + markPath = tick.getMarkPath(x, y, tickLength, tickWidth * reverseCrisp, horiz, renderer); if (mark) { // updating mark.animate({ - d: markPath + d: markPath, + opacity: opacity }); } else { // first time tick.mark = renderer.path( markPath ).attr({ stroke: tickColor, - 'stroke-width': tickWidth + 'stroke-width': tickWidth, + opacity: opacity }).add(axis.axisGroup); } } @@ -5581,12 +6011,12 @@ } // Set the new position, and show or hide - if (show) { + if (show && !isNaN(xy.y)) { + xy.opacity = opacity; label[tick.isNew ? 'attr' : 'animate'](xy); - label.show(); tick.isNew = false; } else { - label.hide(); + label.attr('y', -9999); // #1338 } } }, @@ -5610,9 +6040,6 @@ this.options = options; this.id = options.id; } - - //plotLine.render() - return this; } PlotLineOrBand.prototype = { @@ -5782,7 +6209,8 @@ /** * The class for stack items */ -function StackItem(axis, options, isNegative, x, stackOption) { +function StackItem(axis, options, isNegative, x, stackOption, stacking) { + var inverted = axis.chart.inverted; this.axis = axis; @@ -5796,8 +6224,9 @@ // Save the x value to be able to position the label later this.x = x; - // Save the stack option on the series configuration object + // Save the stack option on the series configuration object, and whether to treat it as percent this.stack = stackOption; + this.percent = stacking === 'percent'; // The align options and text align varies on whether the stack is negative and // if the chart is inverted or not. @@ -5830,7 +6259,11 @@ * Renders the stack total label and adds it to the stack label group. */ render: function (group) { - var str = this.options.formatter.call(this); // format the text in the label + var options = this.options, + formatOption = options.format, // docs: added stackLabel.format option + str = formatOption ? + format(formatOption, this) : + options.formatter.call(this); // format the text in the label // Change the text to reflect the new total and set visibility to hidden in case the serie is hidden if (this.label) { @@ -5838,11 +6271,13 @@ // Create new label } else { this.label = - this.axis.chart.renderer.text(str, 0, 0) // dummy positions, actual position updated with setOffset method in columnseries - .css(this.options.style) // apply style - .attr({align: this.textAlign, // fix the text-anchor - rotation: this.options.rotation, // rotation - visibility: HIDDEN }) // hidden until setOffset is called + this.axis.chart.renderer.text(str, 0, 0, options.useHTML) // dummy positions, actual position updated with setOffset method in columnseries + .css(options.style) // apply style + .attr({ + align: this.textAlign, // fix the text-anchor + rotation: options.rotation, // rotation + visibility: HIDDEN // hidden until setOffset is called + }) .add(group); // add to the labels-group } }, @@ -5856,24 +6291,31 @@ chart = axis.chart, inverted = chart.inverted, neg = this.isNegative, // special treatment is needed for negative stacks - y = axis.translate(this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates + y = axis.translate(this.percent ? 100 : this.total, 0, 0, 0, 1), // stack value translated mapped to chart coordinates yZero = axis.translate(0), // stack origin h = mathAbs(y - yZero), // stack height x = chart.xAxis[0].translate(this.x) + xOffset, // stack x position plotHeight = chart.plotHeight, stackBox = { // this is the box for the complete stack - x: inverted ? (neg ? y : y - h) : x, - y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y), - width: inverted ? h : xWidth, - height: inverted ? xWidth : h - }; - - if (this.label) { - this.label - .align(this.alignOptions, null, stackBox) // align the label to the box - .attr({visibility: VISIBLE}); // set visibility - } + x: inverted ? (neg ? y : y - h) : x, + y: inverted ? plotHeight - x - xWidth : (neg ? (plotHeight - y - h) : plotHeight - y), + width: inverted ? h : xWidth, + height: inverted ? xWidth : h + }, + label = this.label, + alignAttr; + if (label) { + label.align(this.alignOptions, null, stackBox); // align the label to the box + + // Set visibility (#678) + alignAttr = label.alignAttr; + label.attr({ + visibility: this.options.crop === false || chart.isInsidePlot(alignAttr.x, alignAttr.y) ? + (hasSVG ? 'inherit' : VISIBLE) : + HIDDEN + }); + } } }; /** @@ -5960,7 +6402,7 @@ //rotation: 0, //side: 'outside', style: { - color: '#6D869F', + color: '#4d759e', //font: defaultFont.replace('normal', 'bold') fontWeight: 'bold' } @@ -5990,7 +6432,7 @@ tickWidth: 0, title: { rotation: 270, - text: 'Y-values' + text: 'Values' }, stackLabels: { enabled: false, @@ -6001,7 +6443,7 @@ //textAlign: dynamic, //rotation: 0, formatter: function () { - return this.total; + return numberFormat(this.total, -1); }, style: defaultLabelOptions.style } @@ -6109,9 +6551,10 @@ axis.chart = chart; axis.reversed = options.reversed; + axis.zoomEnabled = options.zoomEnabled !== false; // Initial categories - axis.categories = options.categories; + axis.categories = options.categories || type === 'category'; // Elements //axis.axisGroup = UNDEFINED; @@ -6119,16 +6562,15 @@ //axis.axisTitle = UNDEFINED; //axis.axisLine = UNDEFINED; - // Flag if type === logarithmic + // Shorthand types axis.isLog = type === 'logarithmic'; + axis.isDatetimeAxis = isDatetimeAxis; // Flag, if axis is linked to another axis axis.isLinked = defined(options.linkedTo); // Linked axis. //axis.linkedParent = UNDEFINED; - // Flag if type === datetime - axis.isDatetimeAxis = isDatetimeAxis; // Flag if percentage mode //axis.usePercentage = UNDEFINED; @@ -6139,6 +6581,8 @@ // Tick intervals //axis.tickInterval = UNDEFINED; //axis.minorTickInterval = UNDEFINED; + + axis.tickmarkOffset = (axis.categories && options.tickmarkPlacement === 'between') ? 0.5 : 0; // Major ticks axis.ticks = {}; @@ -6175,6 +6619,7 @@ // Dictionary for stacks axis.stacks = {}; + axis._stacksTouched = 0; // Min and max in the data //axis.dataMin = UNDEFINED, @@ -6194,10 +6639,12 @@ events = axis.options.events; // Register - chart.axes.push(axis); - chart[isXAxis ? 'xAxis' : 'yAxis'].push(axis); + if (inArray(axis, chart.axes) === -1) { // don't add it again on Axis.update() + chart.axes.push(axis); + chart[isXAxis ? 'xAxis' : 'yAxis'].push(axis); + } - axis.series = []; // populated by Series + axis.series = axis.series || []; // populated by Series // inverted charts have reversed xAxes as default if (chart.inverted && isXAxis && axis.reversed === UNDEFINED) { @@ -6206,8 +6653,6 @@ axis.removePlotBand = axis.removePlotBandOrLine; axis.removePlotLine = axis.removePlotBandOrLine; - axis.addPlotBand = axis.addPlotBandOrLine; - axis.addPlotLine = axis.addPlotBandOrLine; // register event listeners @@ -6231,10 +6676,58 @@ this.isXAxis ? {} : this.defaultYAxisOptions, [this.defaultTopAxisOptions, this.defaultRightAxisOptions, this.defaultBottomAxisOptions, this.defaultLeftAxisOptions][this.side], - userOptions + merge( + defaultOptions[this.isXAxis ? 'xAxis' : 'yAxis'], // if set in setOptions (#1053) + userOptions + ) ); }, + + /** + * Update the axis with a new options structure + */ + update: function (newOptions, redraw) { + var chart = this.chart; + + newOptions = chart.options[this.xOrY + 'Axis'][this.options.index] = merge(this.userOptions, newOptions); + + this.destroy(); + this._addedPlotLB = false; // #1611 + + this.init(chart, newOptions); + + chart.isDirtyBox = true; + if (pick(redraw, true)) { + chart.redraw(); + } + }, + /** + * Remove the axis from the chart + */ + remove: function (redraw) { + var chart = this.chart, + key = this.xOrY + 'Axis'; // xAxis or yAxis + + // Remove associated series + each(this.series, function (series) { + series.remove(false); + }); + + // Remove the axis + erase(chart.axes, this); + erase(chart[key], this); + chart.options[key].splice(this.options.index, 1); + each(chart[key], function (axis, i) { // Re-index, #1706 + axis.options.index = i; + }); + this.destroy(); + chart.isDirtyBox = true; + + if (pick(redraw, true)) { + chart.redraw(); + } + }, /** * The default label formatter. The context is a special config object for the label. @@ -6242,29 +6735,47 @@ defaultLabelFormatter: function () { var axis = this.axis, value = this.value, - categories = axis.categories, - tickInterval = axis.tickInterval, + categories = axis.categories, dateTimeLabelFormat = this.dateTimeLabelFormat, - ret; + numericSymbols = defaultOptions.lang.numericSymbols, + i = numericSymbols && numericSymbols.length, + multi, + ret, + formatOption = axis.options.labels.format, + + // make sure the same symbol is added for all labels on a linear axis + numericSymbolDetector = axis.isLog ? value : axis.tickInterval; - if (categories) { + if (formatOption) { + ret = format(formatOption, this); + + } else if (categories) { ret = value; } else if (dateTimeLabelFormat) { // datetime axis ret = dateFormat(dateTimeLabelFormat, value); + + } else if (i && numericSymbolDetector >= 1000) { + // Decide whether we should add a numeric symbol like k (thousands) or M (millions). + // If we are to enable this in tooltip or other places as well, we can move this + // logic to the numberFormatter and enable it by a parameter. + while (i-- && ret === UNDEFINED) { + multi = Math.pow(1000, i + 1); + if (numericSymbolDetector >= multi && numericSymbols[i] !== null) { + ret = numberFormat(value / multi, -1) + numericSymbols[i]; + } + } + } + + if (ret === UNDEFINED) { + if (value >= 1000) { // add thousands separators + ret = numberFormat(value, 0); - } else if (tickInterval % 1000000 === 0) { // use M abbreviation - ret = (value / 1000000) + 'M'; - - } else if (tickInterval % 1000 === 0) { // use k abbreviation - ret = (value / 1000) + 'k'; - - } else if (value >= 1000) { // add thousands separators - ret = numberFormat(value, 0); - - } else { // small numbers - ret = numberFormat(value, -1); + } else { // small numbers + ret = numberFormat(value, -1); + } } + return ret; }, @@ -6277,7 +6788,11 @@ stacks = axis.stacks, posStack = [], negStack = [], + stacksTouched = axis._stacksTouched = axis._stacksTouched + 1, + type, i; + + axis.hasVisibleSeries = false; // reset dataMin and dataMax in case we're redrawing axis.dataMin = axis.dataMax = null; @@ -6301,8 +6816,12 @@ threshold = seriesOptions.threshold, yDataLength, activeYData = [], + seriesDataMin, + seriesDataMax, activeCounter = 0; + axis.hasVisibleSeries = true; + // Validate threshold in logarithmic axes if (axis.isLog && threshold <= 0) { threshold = seriesOptions.threshold = null; @@ -6328,7 +6847,6 @@ j, hasModifyValue = !!series.modifyValue; - // Handle stacking stacking = seriesOptions.stacking; axis.usePercentage = stacking === 'percent'; @@ -6362,47 +6880,49 @@ for (i = 0; i < yDataLength; i++) { x = xData[i]; y = yData[i]; - if (y !== null && y !== UNDEFINED) { + + // Read stacked values into a stack based on the x value, + // the sign of y and the stack key. Stacking is also handled for null values (#739) + if (stacking) { + isNegative = y < threshold; + pointStack = isNegative ? negPointStack : posPointStack; + key = isNegative ? negKey : stackKey; + + // Set the stack value and y for extremes + if (defined(pointStack[x])) { // we're adding to the stack + pointStack[x] = correctFloat(pointStack[x] + y); + y = [y, pointStack[x]]; // consider both the actual value and the stack (#1376) - // read stacked values into a stack based on the x value, - // the sign of y and the stack key - if (stacking) { - isNegative = y < threshold; - pointStack = isNegative ? negPointStack : posPointStack; - key = isNegative ? negKey : stackKey; - - y = pointStack[x] = - defined(pointStack[x]) ? - pointStack[x] + y : y; - - - // add the series - if (!stacks[key]) { - stacks[key] = {}; - } + } else { // it's the first point in the stack + pointStack[x] = y; + } - // If the StackItem is there, just update the values, - // if not, create one first - if (!stacks[key][x]) { - stacks[key][x] = new StackItem(axis, axis.options.stackLabels, isNegative, x, stackOption); - } - stacks[key][x].setTotal(y); + // add the series + if (!stacks[key]) { + stacks[key] = {}; + } + // If the StackItem is there, just update the values, + // if not, create one first + if (!stacks[key][x]) { + stacks[key][x] = new StackItem(axis, axis.options.stackLabels, isNegative, x, stackOption, stacking); + } + stacks[key][x].setTotal(pointStack[x]); + stacks[key][x].touched = stacksTouched; + } + + // Handle non null values + if (y !== null && y !== UNDEFINED && (!axis.isLog || (y.length || y > 0))) { // general hook, used for Highstock compare values feature - } else if (hasModifyValue) { + if (hasModifyValue) { y = series.modifyValue(y); } - // get the smallest distance between points - /*if (i) { - distance = mathAbs(xData[i] - xData[i - 1]); - pointRange = pointRange === UNDEFINED ? distance : mathMin(distance, pointRange); - }*/ - - // for points within the visible range, including the first point outside the + // For points within the visible range, including the first point outside the // visible range, consider y extremes - if (cropped || ((xData[i + 1] || x) >= xExtremes.min && (xData[i - 1] || x) <= xExtremes.max)) { + if (series.getExtremesFromAll || cropped || ((xData[i + 1] || x) >= xExtremes.min && + (xData[i - 1] || x) <= xExtremes.max)) { j = y.length; if (j) { // array, like ohlc or range data @@ -6418,17 +6938,13 @@ } } - // record the least unit distance - /*if (findPointRange) { - series.pointRange = pointRange || 1; - } - series.closestPointRange = pointRange;*/ - // Get the dataMin and dataMax so far. If percentage is used, the min and max are // always 0 and 100. If the length of activeYData is 0, continue with null values. if (!axis.usePercentage && activeYData.length) { - axis.dataMin = mathMin(pick(axis.dataMin, activeYData[0]), arrayMin(activeYData)); - axis.dataMax = mathMax(pick(axis.dataMax, activeYData[0]), arrayMax(activeYData)); + series.dataMin = seriesDataMin = arrayMin(activeYData); + series.dataMax = seriesDataMax = arrayMax(activeYData); + axis.dataMin = mathMin(pick(axis.dataMin, seriesDataMin), seriesDataMin); + axis.dataMax = mathMax(pick(axis.dataMax, seriesDataMax), seriesDataMax); } // Adjust to threshold @@ -6444,13 +6960,24 @@ } } }); + + // Destroy unused stacks (#1044) + for (type in stacks) { + for (i in stacks[type]) { + if (stacks[type][i].touched < stacksTouched) { + stacks[type][i].destroy(); + delete stacks[type][i]; + } + } + } + }, /** * Translate from axis value to pixel position on the chart, or back * */ - translate: function (val, backwards, cvsCoord, old, handleLog) { + translate: function (val, backwards, cvsCoord, old, handleLog, pointPlacementBetween) { var axis = this, axisLength = axis.len, sign = 1, @@ -6458,49 +6985,77 @@ localA = old ? axis.oldTransA : axis.transA, localMin = old ? axis.oldMin : axis.min, returnValue, - postTranslate = axis.options.ordinal || (axis.isLog && handleLog); + minPixelPadding = axis.minPixelPadding, + postTranslate = (axis.options.ordinal || (axis.isLog && handleLog)) && axis.lin2val; if (!localA) { localA = axis.transA; } + // In vertical axes, the canvas coordinates start from 0 at the top like in + // SVG. if (cvsCoord) { sign *= -1; // canvas coordinates inverts the value cvsOffset = axisLength; } - if (axis.reversed) { // reversed axis + + // Handle reversed axis + if (axis.reversed) { sign *= -1; cvsOffset -= sign * axisLength; } + // From pixels to value if (backwards) { // reverse translation - if (axis.reversed) { - val = axisLength - val; - } + + val = val * sign + cvsOffset; + val -= minPixelPadding; returnValue = val / localA + localMin; // from chart pixel to value if (postTranslate) { // log and ordinal axes returnValue = axis.lin2val(returnValue); } - } else { // normal translation, from axis value to pixel, relative to plot + // From value to pixels + } else { if (postTranslate) { // log and ordinal axes val = axis.val2lin(val); } - returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * axis.minPixelPadding); + returnValue = sign * (val - localMin) * localA + cvsOffset + (sign * minPixelPadding) + + (pointPlacementBetween ? localA * axis.pointRange / 2 : 0); } return returnValue; }, /** + * Utility method to translate an axis value to pixel position. + * @param {Number} value A value in terms of axis units + * @param {Boolean} paneCoordinates Whether to return the pixel coordinate relative to the chart + * or just the axis/pane itself. + */ + toPixels: function (value, paneCoordinates) { + return this.translate(value, false, !this.horiz, null, true) + (paneCoordinates ? 0 : this.pos); + }, + + /* + * Utility method to translate a pixel position in to an axis value + * @param {Number} pixel The pixel value coordinate + * @param {Boolean} paneCoordiantes Whether the input pixel is relative to the chart or just the + * axis/pane itself. + */ + toValue: function (pixel, paneCoordinates) { + return this.translate(pixel - (paneCoordinates ? 0 : this.pos), true, !this.horiz, null, true); + }, + + /** * Create the path for a plot line that goes from the given value on * this axis, across the plot to the opposite side * @param {Number} value * @param {Number} lineWidth Used for calculation crisp line * @param {Number] old Use old coordinates (for resizing and rescaling) */ - getPlotLinePath: function (value, lineWidth, old) { + getPlotLinePath: function (value, lineWidth, old, force) { var axis = this, chart = axis.chart, axisLeft = axis.left, @@ -6535,7 +7090,7 @@ skip = true; } } - return skip ? + return skip && !force ? null : chart.renderer.crispLine([M, x1, y1, L, x2, y2], lineWidth || 0); }, @@ -6600,11 +7155,10 @@ getLogTickPositions: function (interval, min, max, minor) { var axis = this, options = axis.options, - axisLength = axis.len; - - // Since we use this method for both major and minor ticks, - // use a local variable and return the result - var positions = []; + axisLength = axis.len, + // Since we use this method for both major and minor ticks, + // use a local variable and return the result + positions = []; // Reset if (!minor) { @@ -6641,7 +7195,7 @@ for (j = 0; j < len && !break2; j++) { pos = log2lin(lin2log(i) * intermediate[j]); - if (pos > min) { + if (pos > min && (!minor || lastPos <= max)) { // #1670 positions.push(lastPos); } @@ -6699,10 +7253,10 @@ */ getMinorTickPositions: function () { var axis = this, + options = axis.options, tickPositions = axis.tickPositions, - minorTickInterval = axis.minorTickInterval; - - var minorTickPositions = [], + minorTickInterval = axis.minorTickInterval, + minorTickPositions = [], pos, i, len; @@ -6714,13 +7268,23 @@ axis.getLogTickPositions(minorTickInterval, tickPositions[i - 1], tickPositions[i], true) ); } - + } else if (axis.isDatetimeAxis && options.minorTickInterval === 'auto') { // #1314 + minorTickPositions = minorTickPositions.concat( + getTimeTicks( + normalizeTimeTickInterval(minorTickInterval), + axis.min, + axis.max, + options.startOfWeek + ) + ); + if (minorTickPositions[0] < axis.min) { + minorTickPositions.shift(); + } } else { for (pos = axis.min + (tickPositions[0] - axis.min) % minorTickInterval; pos <= axis.max; pos += minorTickInterval) { - minorTickPositions.push(pos); + minorTickPositions.push(pos); } } - return minorTickPositions; }, @@ -6804,22 +7368,50 @@ /** * Update translation information */ - setAxisTranslation: function () { + setAxisTranslation: function (saveOld) { var axis = this, range = axis.max - axis.min, pointRange = 0, closestPointRange, - seriesClosestPointRange, + minPointOffset = 0, + pointRangePadding = 0, + linkedParent = axis.linkedParent, + ordinalCorrection, transA = axis.transA; // adjust translation for padding if (axis.isXAxis) { - if (axis.isLinked) { - pointRange = axis.linkedParent.pointRange; + if (linkedParent) { + minPointOffset = linkedParent.minPointOffset; + pointRangePadding = linkedParent.pointRangePadding; + } else { each(axis.series, function (series) { - pointRange = mathMax(pointRange, series.pointRange); - seriesClosestPointRange = series.closestPointRange; + var seriesPointRange = series.pointRange, + pointPlacement = series.options.pointPlacement, + seriesClosestPointRange = series.closestPointRange; + + if (seriesPointRange > range) { // #1446 + seriesPointRange = 0; + } + pointRange = mathMax(pointRange, seriesPointRange); + + // minPointOffset is the value padding to the left of the axis in order to make + // room for points with a pointRange, typically columns. When the pointPlacement option + // is 'between' or 'on', this padding does not apply. + minPointOffset = mathMax( + minPointOffset, + pointPlacement ? 0 : seriesPointRange / 2 + ); + + // Determine the total padding needed to the length of the axis to make room for the + // pointRange. If the series' pointPlacement is 'on', no padding is added. + pointRangePadding = mathMax( + pointRangePadding, + pointPlacement === 'on' ? 0 : seriesPointRange + ); + + // Set the closestPointRange if (!series.noSharedTooltip && defined(seriesClosestPointRange)) { closestPointRange = defined(closestPointRange) ? mathMin(closestPointRange, seriesClosestPointRange) : @@ -6827,9 +7419,14 @@ } }); } + + // Record minPointOffset and pointRangePadding + ordinalCorrection = axis.ordinalSlope && closestPointRange ? axis.ordinalSlope / closestPointRange : 1; // #988, #1853 + axis.minPointOffset = minPointOffset = minPointOffset * ordinalCorrection; + axis.pointRangePadding = pointRangePadding = pointRangePadding * ordinalCorrection; // pointRange means the width reserved for each point, like in a column chart - axis.pointRange = pointRange; + axis.pointRange = mathMin(pointRange, range); // closestPointRange means the closest distance between points. In columns // it is mostly equal to pointRange, but in lines pointRange is 0 while closestPointRange @@ -6837,11 +7434,13 @@ axis.closestPointRange = closestPointRange; } - // secondary values - axis.oldTransA = transA; - axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRange) || 1); + // Secondary values + if (saveOld) { + axis.oldTransA = transA; + } + axis.translationSlope = axis.transA = transA = axis.len / ((range + pointRangePadding) || 1); axis.transB = axis.horiz ? axis.left : axis.bottom; // translation addend - axis.minPixelPadding = transA * (pointRange / 2); + axis.minPixelPadding = transA * minPointOffset; }, /** @@ -6863,6 +7462,7 @@ length, linkedParentExtremes, tickIntervalOption = options.tickInterval, + minTickIntervalOption = options.minTickInterval, tickPixelIntervalOption = options.tickPixelInterval, tickPositions, categories = axis.categories; @@ -6897,18 +7497,26 @@ axis.range = null; // don't use it when running setExtremes } } + + // Hook for adjusting this.min and this.max. Used by bubble series. + if (axis.beforePadding) { + axis.beforePadding(); + } // adjust min and max for the minimum range axis.adjustForMinRange(); - - // pad the values to get clear of the chart's edges + + // Pad the values to get clear of the chart's edges. To avoid tickInterval taking the padding + // into account, we do this after computing tick interval (#1337). if (!categories && !axis.usePercentage && !isLinked && defined(axis.min) && defined(axis.max)) { - length = (axis.max - axis.min) || 1; - if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) { - axis.min -= length * minPadding; - } - if (!defined(options.max) && !defined(axis.userMax) && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) { - axis.max += length * maxPadding; + length = axis.max - axis.min; + if (length) { + if (!defined(options.min) && !defined(axis.userMin) && minPadding && (axis.dataMin < 0 || !axis.ignoreMinPadding)) { + axis.min -= length * minPadding; + } + if (!defined(options.max) && !defined(axis.userMax) && maxPadding && (axis.dataMax > 0 || !axis.ignoreMaxPadding)) { + axis.max += length * maxPadding; + } } } @@ -6936,9 +7544,9 @@ } // set the translation factor used in translate function - axis.setAxisTranslation(); + axis.setAxisTranslation(true); - // hook for ordinal axes. To do: merge with below + // hook for ordinal axes and radial axes if (axis.beforeSetTickPositions) { axis.beforeSetTickPositions(); } @@ -6947,11 +7555,16 @@ if (axis.postProcessTickInterval) { axis.tickInterval = axis.postProcessTickInterval(axis.tickInterval); } + + // Before normalizing the tick interval, handle minimum tick interval. This applies only if tickInterval is not defined. + if (!tickIntervalOption && axis.tickInterval < minTickIntervalOption) { + axis.tickInterval = minTickIntervalOption; + } // for linear axes, get magnitude and normalize the interval if (!isDatetimeAxis && !isLog) { // linear magnitude = math.pow(10, mathFloor(math.log(axis.tickInterval) / math.LN10)); - if (!defined(options.tickInterval)) { + if (!tickIntervalOption) { axis.tickInterval = normalizeTickInterval(axis.tickInterval, null, magnitude, options); } } @@ -6961,7 +7574,9 @@ axis.tickInterval / 5 : options.minorTickInterval; // find the tick positions - axis.tickPositions = tickPositions = options.tickPositions || (tickPositioner && tickPositioner.apply(axis, [axis.min, axis.max])); + axis.tickPositions = tickPositions = options.tickPositions ? + [].concat(options.tickPositions) : // Work on a copy (#1565) + (tickPositioner && tickPositioner.apply(axis, [axis.min, axis.max])); if (!tickPositions) { if (isDatetimeAxis) { tickPositions = (axis.getNonLinearTimeTicks || getTimeTicks)( @@ -6985,20 +7600,30 @@ // reset min/max or remove extremes based on start/end on tick var roundedMin = tickPositions[0], - roundedMax = tickPositions[tickPositions.length - 1]; + roundedMax = tickPositions[tickPositions.length - 1], + minPointOffset = axis.minPointOffset || 0, + singlePad; if (options.startOnTick) { axis.min = roundedMin; - } else if (axis.min > roundedMin) { + } else if (axis.min - minPointOffset > roundedMin) { tickPositions.shift(); } if (options.endOnTick) { axis.max = roundedMax; - } else if (axis.max < roundedMax) { + } else if (axis.max + minPointOffset < roundedMax) { tickPositions.pop(); } + // When there is only one point, or all points have the same value on this axis, then min + // and max are equal and tickPositions.length is 1. In this case, add some padding + // in order to center the point, but leave it with one tick. #1337. + if (tickPositions.length === 1) { + singlePad = 0.001; // The lowest possible number to avoid extra padding on columns + axis.min -= singlePad; + axis.max += singlePad; + } } }, @@ -7008,19 +7633,12 @@ setMaxTicks: function () { var chart = this.chart, - maxTicks = chart.maxTicks, + maxTicks = chart.maxTicks || {}, tickPositions = this.tickPositions, - xOrY = this.xOrY; + key = this._maxTicksKey = [this.xOrY, this.pos, this.len].join('-'); - if (!maxTicks) { // first call, or maxTicks have been reset after a zoom operation - maxTicks = { - x: 0, - y: 0 - }; - } - - if (!this.isLinked && !this.isDatetimeAxis && tickPositions.length > maxTicks[xOrY] && this.options.alignTicks !== false) { - maxTicks[xOrY] = tickPositions.length; + if (!this.isLinked && !this.isDatetimeAxis && tickPositions && tickPositions.length > (maxTicks[key] || 0) && this.options.alignTicks !== false) { + maxTicks[key] = tickPositions.length; } chart.maxTicks = maxTicks; }, @@ -7032,17 +7650,17 @@ adjustTickAmount: function () { var axis = this, chart = axis.chart, - xOrY = axis.xOrY, + key = axis._maxTicksKey, tickPositions = axis.tickPositions, maxTicks = chart.maxTicks; - if (maxTicks && maxTicks[xOrY] && !axis.isDatetimeAxis && !axis.categories && !axis.isLinked && axis.options.alignTicks !== false) { // only apply to linear scale + if (maxTicks && maxTicks[key] && !axis.isDatetimeAxis && !axis.categories && !axis.isLinked && axis.options.alignTicks !== false) { // only apply to linear scale var oldTickAmount = axis.tickAmount, calculatedTickAmount = tickPositions.length, tickAmount; // set the axis-level tickAmount to use below - axis.tickAmount = tickAmount = maxTicks[xOrY]; + axis.tickAmount = tickAmount = maxTicks[key]; if (calculatedTickAmount < tickAmount) { while (tickPositions.length < tickAmount) { @@ -7090,9 +7708,11 @@ }); // do we really need to go through all this? - if (isDirtyAxisLength || isDirtyData || axis.isLinked || + if (isDirtyAxisLength || isDirtyData || axis.isLinked || axis.forceRedraw || axis.userMin !== axis.oldUserMin || axis.userMax !== axis.oldUserMax) { + axis.forceRedraw = false; + // get data extremes if needed axis.getSeriesExtremes(); @@ -7162,25 +7782,60 @@ }, /** + * Overridable method for zooming chart. Pulled out in a separate method to allow overriding + * in stock charts. + */ + zoom: function (newMin, newMax) { + + // Prevent pinch zooming out of range + if (!this.allowZoomOutside) { + if (newMin <= this.dataMin) { + newMin = UNDEFINED; + } + if (newMax >= this.dataMax) { + newMax = UNDEFINED; + } + } + + // In full view, displaying the reset zoom button is not required + this.displayBtn = newMin !== UNDEFINED || newMax !== UNDEFINED; + + // Do it + this.setExtremes( + newMin, + newMax, + false, + UNDEFINED, + { trigger: 'zoom' } + ); + return true; + }, + + /** * Update the axis metrics */ setAxisSize: function () { - var axis = this, - chart = axis.chart, - options = axis.options; - - var offsetLeft = options.offsetLeft || 0, - offsetRight = options.offsetRight || 0; + var chart = this.chart, + options = this.options, + offsetLeft = options.offsetLeft || 0, + offsetRight = options.offsetRight || 0, + horiz = this.horiz, + width, + height, + top, + left; - // basic values - // expose to use in Series object and navigator - axis.left = pick(options.left, chart.plotLeft + offsetLeft); - axis.top = pick(options.top, chart.plotTop); - axis.width = pick(options.width, chart.plotWidth - offsetLeft + offsetRight); - axis.height = pick(options.height, chart.plotHeight); - axis.bottom = chart.chartHeight - axis.height - axis.top; - axis.right = chart.chartWidth - axis.width - axis.left; - axis.len = mathMax(axis.horiz ? axis.width : axis.height, 0); // mathMax fixes #905 + // Expose basic values to use in Series object and navigator + this.left = left = pick(options.left, chart.plotLeft + offsetLeft); + this.top = top = pick(options.top, chart.plotTop); + this.width = width = pick(options.width, chart.plotWidth - offsetLeft + offsetRight); + this.height = height = pick(options.height, chart.plotHeight); + this.bottom = chart.chartHeight - height - top; + this.right = chart.chartWidth - width - left; + + // Direction agnostic properties + this.len = mathMax(horiz ? width : height, 0); // mathMax fixes #905 + this.pos = horiz ? left : top; // distance from SVG origin }, /** @@ -7220,14 +7875,31 @@ return axis.translate(threshold, 0, 1, 0, 1); }, + addPlotBand: function (options) { + this.addPlotBandOrLine(options, 'plotBands'); + }, + + addPlotLine: function (options) { + this.addPlotBandOrLine(options, 'plotLines'); + }, + /** * Add a plot band or plot line after render time * * @param options {Object} The plotBand or plotLine configuration object */ - addPlotBandOrLine: function (options) { - var obj = new PlotLineOrBand(this, options).render(); - this.plotLinesAndBands.push(obj); + addPlotBandOrLine: function (options, coll) { + var obj = new PlotLineOrBand(this, options).render(), + userOptions = this.userOptions; + + // Add it to the user options for exporting and Axis.update + if (coll) { + userOptions[coll] = userOptions[coll] || []; + userOptions[coll].push(options); + } + + this.plotLinesAndBands.push(obj); + return obj; }, @@ -7243,6 +7915,7 @@ ticks = axis.ticks, horiz = axis.horiz, side = axis.side, + invertedSide = chart.inverted ? [1, 0, 3, 2][side] : side, hasData, showAxis, titleOffset = 0, @@ -7252,22 +7925,25 @@ labelOptions = options.labels, labelOffset = 0, // reset axisOffset = chart.axisOffset, + clipOffset = chart.clipOffset, directionFactor = [-1, 1, 1, -1][side], n; - // For reuse in Axis.render - axis.hasData = hasData = axis.series.length && defined(axis.min) && defined(axis.max); + axis.hasData = hasData = (axis.hasVisibleSeries || (defined(axis.min) && defined(axis.max) && !!tickPositions)); axis.showAxis = showAxis = hasData || pick(options.showEmpty, true); - + // Create the axisGroup and gridGroup elements on first iteration if (!axis.axisGroup) { - axis.axisGroup = renderer.g('axis') - .attr({ zIndex: options.zIndex || 7 }) - .add(); axis.gridGroup = renderer.g('grid') .attr({ zIndex: options.gridZIndex || 1 }) .add(); + axis.axisGroup = renderer.g('axis') + .attr({ zIndex: options.zIndex || 2 }) + .add(); + axis.labelGroup = renderer.g('axis-labels') + .attr({ zIndex: labelOptions.zIndex || 7 }) + .add(); } if (hasData || axis.isLinked) { @@ -7304,7 +7980,7 @@ } } - if (axisTitleOptions && axisTitleOptions.text) { + if (axisTitleOptions && axisTitleOptions.text && axisTitleOptions.enabled !== false) { if (!axis.axisTitle) { axis.axisTitle = renderer.text( axisTitleOptions.text, @@ -7337,7 +8013,6 @@ // handle automatic or user set offset axis.offset = directionFactor * pick(options.offset, axisOffset[side]); - axis.axisTitleMargin = pick(titleOffsetOption, labelOffset + titleMargin + @@ -7348,6 +8023,7 @@ axisOffset[side], axis.axisTitleMargin + titleOffset + directionFactor * axis.offset ); + clipOffset[invertedSide] = mathMax(clipOffset[invertedSide], options.lineWidth); }, @@ -7361,6 +8037,11 @@ horiz = this.horiz, lineLeft = this.left + (opposite ? this.width : 0) + offset, lineTop = chart.chartHeight - this.bottom - (opposite ? this.height : 0) + offset; + + this.lineTop = lineTop; // used by flag series + if (!opposite) { + lineWidth *= -1; // crispify the other way - #1480 + } return chart.renderer.crispLine([ M, @@ -7438,6 +8119,7 @@ alternateBands = axis.alternateBands, stackLabelOptions = options.stackLabels, alternateGridColor = options.alternateGridColor, + tickmarkOffset = axis.tickmarkOffset, lineWidth = options.lineWidth, linePath, hasRendered = chart.hasRendered, @@ -7447,6 +8129,14 @@ from, to; + // Mark all elements inActive before we go over and mark the active ones + each([ticks, minorTicks, alternateBands], function (coll) { + var pos; + for (pos in coll) { + coll[pos].isActive = false; + } + }); + // If the series has data draw the ticks. Else only the line and title if (hasData || isLinked) { @@ -7462,36 +8152,44 @@ minorTicks[pos].render(null, true); } - - minorTicks[pos].isActive = true; - minorTicks[pos].render(); + minorTicks[pos].render(null, false, 1); }); } // Major ticks. Pull out the first item and render it last so that // we can get the position of the neighbour label. #808. - each(tickPositions.slice(1).concat([tickPositions[0]]), function (pos, i) { - - // Reorganize the indices - i = (i === tickPositions.length - 1) ? 0 : i + 1; - - // linked axes need an extra check to find out if - if (!isLinked || (pos >= axis.min && pos <= axis.max)) { - - if (!ticks[pos]) { - ticks[pos] = new Tick(axis, pos); + if (tickPositions.length) { // #1300 + each(tickPositions.slice(1).concat([tickPositions[0]]), function (pos, i) { + + // Reorganize the indices + i = (i === tickPositions.length - 1) ? 0 : i + 1; + + // linked axes need an extra check to find out if + if (!isLinked || (pos >= axis.min && pos <= axis.max)) { + + if (!ticks[pos]) { + ticks[pos] = new Tick(axis, pos); + } + + // render new ticks in old position + if (slideInTicks && ticks[pos].isNew) { + ticks[pos].render(i, true); + } + + ticks[pos].render(i, false, 1); } - - // render new ticks in old position - if (slideInTicks && ticks[pos].isNew) { - ticks[pos].render(i, true); + + }); + // In a categorized axis, the tick marks are displayed between labels. So + // we need to add a tick mark and grid line at the left edge of the X axis. + if (tickmarkOffset && axis.min === 0) { + if (!ticks[-1]) { + ticks[-1] = new Tick(axis, -1, null, true); } - - ticks[pos].isActive = true; - ticks[pos].render(i); + ticks[-1].render(-1); } - - }); + + } // alternate grid color if (alternateGridColor) { @@ -7500,8 +8198,8 @@ if (!alternateBands[pos]) { alternateBands[pos] = new PlotLineOrBand(axis); } - from = pos; - to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] : axis.max; + from = pos + tickmarkOffset; // #949 + to = tickPositions[i + 1] !== UNDEFINED ? tickPositions[i + 1] + tickmarkOffset : axis.max; alternateBands[pos].options = { from: isLog ? lin2log(from) : from, to: isLog ? lin2log(to) : to, @@ -7516,7 +8214,6 @@ // custom plot lines and bands if (!axis._addedPlotLB) { // only first time each((options.plotLines || []).concat(options.plotBands || []), function (plotLineOptions) { - //plotLinesAndBands.push(new PlotLineOrBand(plotLineOptions).render()); axis.addPlotBandOrLine(plotLineOptions); }); axis._addedPlotLB = true; @@ -7524,17 +8221,41 @@ } // end if hasData - // remove inactive ticks + // Remove inactive ticks each([ticks, minorTicks, alternateBands], function (coll) { - var pos; + var pos, + i, + forDestruction = [], + delay = globalAnimation ? globalAnimation.duration || 500 : 0, + destroyInactiveItems = function () { + i = forDestruction.length; + while (i--) { + // When resizing rapidly, the same items may be destroyed in different timeouts, + // or the may be reactivated + if (coll[forDestruction[i]] && !coll[forDestruction[i]].isActive) { + coll[forDestruction[i]].destroy(); + delete coll[forDestruction[i]]; + } + } + + }; + for (pos in coll) { + if (!coll[pos].isActive) { - coll[pos].destroy(); - delete coll[pos]; - } else { - coll[pos].isActive = false; // reset + // Render to zero opacity + coll[pos].render(pos, false, 0); + coll[pos].isActive = false; + forDestruction.push(pos); } } + + // When the objects are finished fading out, destroy them + if (coll === alternateBands || !chart.hasRendered || !delay) { + destroyInactiveItems(); + } else if (delay) { + setTimeout(destroyInactiveItems, delay); + } }); // Static items. As the axis group is cleared on subsequent calls @@ -7549,14 +8270,13 @@ 'stroke-width': lineWidth, zIndex: 7 }) - .add(); + .add(axis.axisGroup); } else { axis.axisLine.animate({ d: linePath }); } // show or hide the line depending on options.showEmpty axis.axisLine[showAxis ? 'show' : 'hide'](); - } if (axisTitle && showAxis) { @@ -7618,19 +8338,7 @@ * Update the axis title by options */ setTitle: function (newTitleOptions, redraw) { - var axis = this, - chart = axis.chart, - options = axis.options, - axisTitle; - - options.title = merge(options.title, newTitleOptions); - - axis.axisTitle = axisTitle && axisTitle.destroy(); // #922 - axis.isDirty = true; - - if (pick(redraw, true)) { - chart.redraw(); - } + this.update({ title: newTitleOptions }, redraw); }, /** @@ -7638,11 +8346,12 @@ */ redraw: function () { var axis = this, - chart = axis.chart; + chart = axis.chart, + pointer = chart.pointer; // hide tooltip and hover states - if (chart.tracker.resetTracker) { - chart.tracker.resetTracker(true); + if (pointer.reset) { + pointer.reset(true); } // render the axis @@ -7662,29 +8371,11 @@ /** * Set new axis categories and optionally redraw - * @param {Array} newCategories - * @param {Boolean} doRedraw + * @param {Array} categories + * @param {Boolean} redraw */ - setCategories: function (newCategories, doRedraw) { - var axis = this, - chart = axis.chart; - - // set the categories - axis.categories = axis.userOptions.categories = newCategories; - - // force reindexing tooltips - each(axis.series, function (series) { - series.translate(); - series.setTooltipPoints(true); - }); - - - // optionally redraw - axis.isDirty = true; - - if (pick(doRedraw, true)) { - chart.redraw(); - } + setCategories: function (categories, redraw) { + this.update({ categories: categories }, redraw); }, /** @@ -7711,7 +8402,7 @@ }); // Destroy local variables - each(['stackTotalGroup', 'axisLine', 'axisGroup', 'gridGroup', 'axisTitle'], function (prop) { + each(['stackTotalGroup', 'axisLine', 'axisGroup', 'gridGroup', 'labelGroup', 'axisTitle'], function (prop) { if (axis[prop]) { axis[prop] = axis[prop].destroy(); } @@ -7726,59 +8417,59 @@ * @param {Object} chart The chart instance * @param {Object} options Tooltip options */ -function Tooltip(chart, options) { - var borderWidth = options.borderWidth, - style = options.style, - shared = options.shared, - padding = pInt(style.padding); +function Tooltip() { + this.init.apply(this, arguments); +} - // Save the chart and options - this.chart = chart; - this.options = options; +Tooltip.prototype = { + + init: function (chart, options) { - // remove padding CSS and apply padding on box instead - style.padding = 0; + var borderWidth = options.borderWidth, + style = options.style, + padding = pInt(style.padding); - // Keep track of the current series - //this.currentSeries = UNDEFINED; + // Save the chart and options + this.chart = chart; + this.options = options; - // List of crosshairs - this.crosshairs = []; + // Keep track of the current series + //this.currentSeries = UNDEFINED; - // Current values of x and y when animating - this.currentX = 0; - this.currentY = 0; - - // The tooltipTick function, initialized to nothing - //this.tooltipTick = UNDEFINED; - - // The tooltip is initially hidden - this.tooltipIsHidden = true; - - // create the label - this.label = chart.renderer.label('', 0, 0, null, null, null, options.useHTML, null, 'tooltip') - .attr({ - padding: padding, - fill: options.backgroundColor, - 'stroke-width': borderWidth, - r: options.borderRadius, - zIndex: 8 - }) - .css(style) - .hide() - .add(); - - // When using canVG the shadow shows up as a gray circle - // even if the tooltip is hidden. - if (!useCanVG) { - this.label.shadow(options.shadow); - } + // List of crosshairs + this.crosshairs = []; - // Public property for getting the shared state. - this.shared = shared; -} + // Current values of x and y when animating + this.now = { x: 0, y: 0 }; + + // The tooltip is initially hidden + this.isHidden = true; + + + // create the label + this.label = chart.renderer.label('', 0, 0, options.shape, null, null, options.useHTML, null, 'tooltip') + .attr({ + padding: padding, + fill: options.backgroundColor, + 'stroke-width': borderWidth, + r: options.borderRadius, + zIndex: 8 + }) + .css(style) + .css({ padding: 0 }) // Remove it from VML, the padding is applied as an attribute instead (#1117) + .hide() + .add(); + + // When using canVG the shadow shows up as a gray circle + // even if the tooltip is hidden. + if (!useCanVG) { + this.label.shadow(options.shadow); + } + + // Public property for getting the shared state. + this.shared = options.shared; + }, -Tooltip.prototype = { /** * Destroy the tooltip and its elements. */ @@ -7793,32 +8484,48 @@ if (this.label) { this.label = this.label.destroy(); } + clearTimeout(this.hideTimer); + clearTimeout(this.tooltipTimeout); }, /** * Provide a soft movement for the tooltip * - * @param {Number} finalX - * @param {Number} finalY + * @param {Number} x + * @param {Number} y * @private */ - move: function (finalX, finalY) { - var tooltip = this; + move: function (x, y, anchorX, anchorY) { + var tooltip = this, + now = tooltip.now, + animate = tooltip.options.animation !== false && !tooltip.isHidden; // get intermediate values for animation - tooltip.currentX = tooltip.tooltipIsHidden ? finalX : (2 * tooltip.currentX + finalX) / 3; - tooltip.currentY = tooltip.tooltipIsHidden ? finalY : (tooltip.currentY + finalY) / 2; + extend(now, { + x: animate ? (2 * now.x + x) / 3 : x, + y: animate ? (now.y + y) / 2 : y, + anchorX: animate ? (2 * now.anchorX + anchorX) / 3 : anchorX, + anchorY: animate ? (now.anchorY + anchorY) / 2 : anchorY + }); // move to the intermediate value - tooltip.label.attr({ x: tooltip.currentX, y: tooltip.currentY }); + tooltip.label.attr(now); + // run on next tick of the mouse tracker - if (mathAbs(finalX - tooltip.currentX) > 1 || mathAbs(finalY - tooltip.currentY) > 1) { - tooltip.tooltipTick = function () { - tooltip.move(finalX, finalY); - }; - } else { - tooltip.tooltipTick = null; + if (animate && (mathAbs(x - now.x) > 1 || mathAbs(y - now.y) > 1)) { + + // never allow two timeouts + clearTimeout(this.tooltipTimeout); + + // set the fixed interval ticking for the smooth tooltip + this.tooltipTimeout = setTimeout(function () { + // The interval function may still be running during destroy, so check that the chart is really there before calling. + if (tooltip) { + tooltip.move(x, y, anchorX, anchorY); + } + }, 32); + } }, @@ -7826,10 +8533,17 @@ * Hide the tooltip */ hide: function () { - if (!this.tooltipIsHidden) { - var hoverPoints = this.chart.hoverPoints; - - this.label.hide(); + var tooltip = this, + hoverPoints; + + clearTimeout(this.hideTimer); // disallow duplicate timers (#1728, #1766) + if (!this.isHidden) { + hoverPoints = this.chart.hoverPoints; + + this.hideTimer = setTimeout(function () { + tooltip.label.fadeOut(); + tooltip.isHidden = true; + }, pick(this.options.hideDelay, 500)); // hide previous hoverPoints and set new if (hoverPoints) { @@ -7839,7 +8553,6 @@ } this.chart.hoverPoints = null; - this.tooltipIsHidden = true; } }, @@ -7862,19 +8575,33 @@ var ret, chart = this.chart, inverted = chart.inverted, + plotTop = chart.plotTop, plotX = 0, - plotY = 0; + plotY = 0, + yAxis; points = splat(points); // Pie uses a special tooltipPos ret = points[0].tooltipPos; + // When tooltip follows mouse, relate the position to the mouse + if (this.followPointer && mouseEvent) { + if (mouseEvent.chartX === UNDEFINED) { + mouseEvent = chart.pointer.normalize(mouseEvent); + } + ret = [ + mouseEvent.chartX - chart.plotLeft, + mouseEvent.chartY - plotTop + ]; + } // When shared, use the average position if (!ret) { each(points, function (point) { + yAxis = point.series.yAxis; plotX += point.plotX; - plotY += point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY; + plotY += (point.plotLow ? (point.plotLow + point.plotHigh) / 2 : point.plotY) + + (!inverted && yAxis ? yAxis.top - plotTop : 0); // #1151 }); plotX /= points.length; @@ -7883,7 +8610,7 @@ ret = [ inverted ? chart.plotWidth - plotY : plotX, this.shared && !inverted && points.length > 1 && mouseEvent ? - mouseEvent.chartY - chart.plotTop : // place shared tooltip next to the mouse (#424) + mouseEvent.chartY - plotTop : // place shared tooltip next to the mouse (#424) inverted ? chart.plotHeight - plotX : plotY ]; } @@ -7912,7 +8639,7 @@ // It is too far to the left, adjust it if (x < 7) { - x = plotLeft + pointX + distance; + x = plotLeft + mathMax(pointX, 0) + distance; } // Test to see if the tooltip is too far to the right, @@ -7938,12 +8665,36 @@ if (y + boxHeight > plotTop + plotHeight) { y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below } - return {x: x, y: y}; }, /** + * In case no user defined formatter is given, this will be used. Note that the context + * here is an object holding point, series, x, y etc. + */ + defaultFormatter: function (tooltip) { + var items = this.points || splat(this), + series = items[0].series, + s; + + // build the header + s = [series.tooltipHeaderFormatter(items[0])]; + + // build the values + each(items, function (item) { + series = item.series; + s.push((series.tooltipFormatter && series.tooltipFormatter(item)) || + item.point.tooltipFormatter(series.tooltipOptions.pointFormat)); + }); + + // footer + s.push(tooltip.options.footerFormat || ''); + + return s.join(''); + }, + + /** * Refresh the tooltip's text and position. * @param {Object} point */ @@ -7951,49 +8702,25 @@ var tooltip = this, chart = tooltip.chart, label = tooltip.label, - options = tooltip.options; - - /** - * In case no user defined formatter is given, this will be used - */ - function defaultFormatter() { - var pThis = this, - items = pThis.points || splat(pThis), - series = items[0].series, - s; - - // build the header - s = [series.tooltipHeaderFormatter(items[0].key)]; - - // build the values - each(items, function (item) { - series = item.series; - s.push((series.tooltipFormatter && series.tooltipFormatter(item)) || - item.point.tooltipFormatter(series.tooltipOptions.pointFormat)); - }); - - // footer - s.push(options.footerFormat || ''); - - return s.join(''); - } - - var x, + options = tooltip.options, + x, y, show, anchor, textConfig = {}, text, pointConfig = [], - formatter = options.formatter || defaultFormatter, + formatter = options.formatter || tooltip.defaultFormatter, hoverPoints = chart.hoverPoints, - placedTooltipPoint, borderColor, crosshairsOptions = options.crosshairs, shared = tooltip.shared, currentSeries; + clearTimeout(this.hideTimer); + // get the reference point coordinates (pie charts use tooltipPos) + tooltip.followPointer = splat(point)[0].series.tooltipOptions.followPointer; anchor = tooltip.getAnchor(point, mouseEvent); x = anchor[0]; y = anchor[1]; @@ -8002,12 +8729,13 @@ if (shared && !(point.series && point.series.noSharedTooltip)) { // hide previous hoverPoints and set new + + chart.hoverPoints = point; if (hoverPoints) { each(hoverPoints, function (point) { point.setState(); }); } - chart.hoverPoints = point; each(point, function (item) { item.setState(HOVER_STATE); @@ -8026,7 +8754,7 @@ } else { textConfig = point.getLabelConfig(); } - text = formatter.call(textConfig); + text = formatter.call(textConfig, tooltip); // register the current series currentSeries = point.series; @@ -8041,8 +8769,9 @@ } else { // show it - if (tooltip.tooltipIsHidden) { - label.show(); + if (tooltip.isHidden) { + stop(label); + label.attr('opacity', 1).show(); } // update text @@ -8055,19 +8784,10 @@ label.attr({ stroke: borderColor }); - - placedTooltipPoint = (options.positioner || tooltip.getPosition).call( - tooltip, - label.width, - label.height, - { plotX: x, plotY: y } - ); - - // do the move - tooltip.move(mathRound(placedTooltipPoint.x), mathRound(placedTooltipPoint.y)); - - tooltip.tooltipIsHidden = false; + tooltip.updatePosition({ plotX: x, plotY: y }); + + this.isHidden = false; } // crosshairs @@ -8077,14 +8797,19 @@ var path, i = crosshairsOptions.length, attribs, - axis; + axis, + val; while (i--) { axis = point.series[i ? 'yAxis' : 'xAxis']; if (crosshairsOptions[i] && axis) { + val = i ? pick(point.stackY, point.y) : point.x; // #814 + if (axis.isLog) { // #1671 + val = log2lin(val); + } path = axis.getPlotLinePath( - i ? pick(point.stackY, point.y) : point.x, // #814 + val, 1 ); @@ -8113,58 +8838,75 @@ borderColor: borderColor }); }, - + /** - * Runs the tooltip animation one tick. + * Find the new position and perform the move */ - tick: function () { - if (this.tooltipTick) { - this.tooltipTick(); - } + updatePosition: function (point) { + var chart = this.chart, + label = this.label, + pos = (this.options.positioner || this.getPosition).call( + this, + label.width, + label.height, + point + ); + + // do the move + this.move( + mathRound(pos.x), + mathRound(pos.y), + point.plotX + chart.plotLeft, + point.plotY + chart.plotTop + ); } }; /** - * The mouse tracker object + * The mouse tracker object. All methods starting with "on" are primary DOM event handlers. + * Subsequent methods should be named differently from what they are doing. * @param {Object} chart The Chart instance * @param {Object} options The root options object */ -function MouseTracker(chart, options) { - var zoomType = useCanVG ? '' : options.chart.zoomType; - - // Zoom status - this.zoomX = /x/.test(zoomType); - this.zoomY = /y/.test(zoomType); - - // Store reference to options - this.options = options; +function Pointer(chart, options) { + this.init(chart, options); +} - // Reference to the chart - this.chart = chart; +Pointer.prototype = { + /** + * Initialize Pointer + */ + init: function (chart, options) { + + var zoomType = useCanVG ? '' : options.chart.zoomType, + inverted = chart.inverted, + zoomX, + zoomY; - // The interval id - //this.tooltipInterval = UNDEFINED; + // Store references + this.options = options; + this.chart = chart; + + // Zoom status + this.zoomX = zoomX = /x/.test(zoomType); + this.zoomY = zoomY = /y/.test(zoomType); + this.zoomHor = (zoomX && !inverted) || (zoomY && inverted); + this.zoomVert = (zoomY && !inverted) || (zoomX && inverted); - // The cached x hover position - //this.hoverX = UNDEFINED; + this.pinchDown = []; + this.lastValidTouch = {}; - // The chart position - //this.chartPosition = UNDEFINED; + if (options.tooltip.enabled) { + chart.tooltip = new Tooltip(chart, options.tooltip); + } - // The selection marker element - //this.selectionMarker = UNDEFINED; - - // False or a value > 0 if a dragging operation - //this.mouseDownX = UNDEFINED; - //this.mouseDownY = UNDEFINED; - this.init(chart, options.tooltip); -} + this.setDOMEvents(); + }, -MouseTracker.prototype = { /** * Add crossbrowser support for chartX and chartY * @param {Object} e The event object in standard browsers */ - normalizeMouseEvent: function (e) { + normalize: function (e) { var chartPosition, chartX, chartY, @@ -8176,16 +8918,9 @@ e.target = e.srcElement; } - // jQuery only copies over some properties. IE needs e.x and iOS needs touches. - if (e.originalEvent) { - e = e.originalEvent; - } - - // The same for MooTools. It renames e.pageX to e.page.x. #445. - if (e.event) { - e = e.event; - } - + // Framework specific normalizing (#1165) + e = washMouseEvent(e); + // iOS ePos = e.touches ? e.touches.item(0) : e; @@ -8204,46 +8939,49 @@ return extend(e, { chartX: mathRound(chartX), chartY: mathRound(chartY) - }); }, /** * Get the click position in terms of axis values. * - * @param {Object} e A mouse event + * @param {Object} e A pointer event */ - getMouseCoordinates: function (e) { + getCoordinates: function (e) { var coordinates = { xAxis: [], yAxis: [] - }, - chart = this.chart; - - each(chart.axes, function (axis) { - var isXAxis = axis.isXAxis, - isHorizontal = chart.inverted ? !isXAxis : isXAxis; + }; - coordinates[isXAxis ? 'xAxis' : 'yAxis'].push({ + each(this.chart.axes, function (axis) { + coordinates[axis.isXAxis ? 'xAxis' : 'yAxis'].push({ axis: axis, - value: axis.translate( - isHorizontal ? - e.chartX - chart.plotLeft : - chart.plotHeight - e.chartY + chart.plotTop, - true - ) + value: axis.toValue(e[axis.horiz ? 'chartX' : 'chartY']) }); }); return coordinates; }, + + /** + * Return the index in the tooltipPoints array, corresponding to pixel position in + * the plot area. + */ + getIndex: function (e) { + var chart = this.chart; + return chart.inverted ? + chart.plotHeight + chart.plotTop - e.chartY : + e.chartX - chart.plotLeft; + }, /** - * With line type charts with a single tracker, get the point closest to the mouse + * With line type charts with a single tracker, get the point closest to the mouse. + * Run Point.onMouseOver and display tooltip for the point or points. */ - onmousemove: function (e) { - var mouseTracker = this, - chart = mouseTracker.chart, + runPointActions: function (e) { + var pointer = this, + chart = pointer.chart, series = chart.series, + tooltip = chart.tooltip, point, points, hoverPoint = chart.hoverPoint, @@ -8251,11 +8989,11 @@ i, j, distance = chart.chartWidth, - // the index in the tooltipPoints array, corresponding to pixel position in plot area - index = chart.inverted ? chart.plotHeight + chart.plotTop - e.chartY : e.chartX - chart.plotLeft; + index = pointer.getIndex(e), + anchor; // shared tooltip - if (chart.tooltip && mouseTracker.options.tooltip.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) { + if (tooltip && pointer.options.tooltip.shared && !(hoverSeries && hoverSeries.noSharedTooltip)) { points = []; // loop over all series and find the ones with points closest to the mouse @@ -8265,9 +9003,11 @@ series[j].options.enableMouseTracking !== false && !series[j].noSharedTooltip && series[j].tooltipPoints.length) { point = series[j].tooltipPoints[index]; - point._dist = mathAbs(index - point.plotX); - distance = mathMin(distance, point._dist); - points.push(point); + if (point.series) { // not a dummy point, #1544 + point._dist = mathAbs(index - point.clientX); + distance = mathMin(distance, point._dist); + points.push(point); + } } } // remove furthest points @@ -8278,9 +9018,9 @@ } } // refresh the tooltip if necessary - if (points.length && (points[0].plotX !== mouseTracker.hoverX)) { - chart.tooltip.refresh(points, e); - mouseTracker.hoverX = points[0].plotX; + if (points.length && (points[0].clientX !== pointer.hoverX)) { + tooltip.refresh(points, e); + pointer.hoverX = points[0].clientX; } } @@ -8294,9 +9034,13 @@ if (point && point !== hoverPoint) { // trigger the events - point.onMouseOver(); + point.onMouseOver(e); } + + } else if (tooltip && tooltip.followPointer && !tooltip.isHidden) { + anchor = tooltip.getAnchor([{}], e); + tooltip.updatePosition({ plotX: anchor[0], plotY: anchor[1] }); } }, @@ -8304,14 +9048,16 @@ /** * Reset the tracking by hiding the tooltip, the hover series state and the hover point + * + * @param allowMove {Boolean} Instead of destroying the tooltip altogether, allow moving it if possible */ - resetTracker: function (allowMove) { - var mouseTracker = this, - chart = mouseTracker.chart, + reset: function (allowMove) { + var pointer = this, + chart = pointer.chart, hoverSeries = chart.hoverSeries, hoverPoint = chart.hoverPoint, - tooltipPoints = chart.hoverPoints || hoverPoint, - tooltip = chart.tooltip; + tooltip = chart.tooltip, + tooltipPoints = tooltip && tooltip.shared ? chart.hoverPoints : hoverPoint; // Narrow in allowMove allowMove = allowMove && tooltip && tooltipPoints; @@ -8341,383 +9087,619 @@ tooltip.hideCrosshairs(); } - mouseTracker.hoverX = null; + pointer.hoverX = null; } }, /** - * Set the JS events on the container element + * Scale series groups to a certain scale and translation */ - setDOMEvents: function () { - var lastWasOutsidePlot = true, - mouseTracker = this, - chart = mouseTracker.chart, - container = chart.container, - hasDragged, - zoomHor = (mouseTracker.zoomX && !chart.inverted) || (mouseTracker.zoomY && chart.inverted), - zoomVert = (mouseTracker.zoomY && !chart.inverted) || (mouseTracker.zoomX && chart.inverted); + scaleGroups: function (attribs, clip) { - /** - * Mouse up or outside the plot area - */ - function drop() { - if (mouseTracker.selectionMarker) { - var selectionData = { - xAxis: [], - yAxis: [] - }, - selectionBox = mouseTracker.selectionMarker.getBBox(), - selectionLeft = selectionBox.x - chart.plotLeft, - selectionTop = selectionBox.y - chart.plotTop, - runZoom; - - // a selection has been made - if (hasDragged) { - - // record each axis' min and max - each(chart.axes, function (axis) { - if (axis.options.zoomEnabled !== false) { - var isXAxis = axis.isXAxis, - isHorizontal = chart.inverted ? !isXAxis : isXAxis, - selectionMin = axis.translate( - isHorizontal ? - selectionLeft : - chart.plotHeight - selectionTop - selectionBox.height, - true, - 0, - 0, - 1 - ), - selectionMax = axis.translate( - isHorizontal ? - selectionLeft + selectionBox.width : - chart.plotHeight - selectionTop, - true, - 0, - 0, - 1 - ); - - if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859 - selectionData[isXAxis ? 'xAxis' : 'yAxis'].push({ - axis: axis, - min: mathMin(selectionMin, selectionMax), // for reversed axes, - max: mathMax(selectionMin, selectionMax) - }); - runZoom = true; - } - } - }); - if (runZoom) { - fireEvent(chart, 'selection', selectionData, function (args) { chart.zoom(args); }); - } + var chart = this.chart; + // Scale each series + each(chart.series, function (series) { + if (series.xAxis && series.xAxis.zoomEnabled) { + series.group.attr(attribs); + if (series.markerGroup) { + series.markerGroup.attr(attribs); + series.markerGroup.clip(clip ? chart.clipRect : null); + } + if (series.dataLabelsGroup) { + series.dataLabelsGroup.attr(attribs); } - mouseTracker.selectionMarker = mouseTracker.selectionMarker.destroy(); } + }); + + // Clip + chart.clipRect.attr(clip || chart.clipBox); + }, - if (chart) { // it may be destroyed on mouse up - #877 - css(container, { cursor: 'auto' }); - chart.cancelClick = hasDragged; // #370 - chart.mouseIsDown = hasDragged = false; - } + /** + * Run translation operations for each direction (horizontal and vertical) independently + */ + pinchTranslateDirection: function (horiz, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch) { + var chart = this.chart, + xy = horiz ? 'x' : 'y', + XY = horiz ? 'X' : 'Y', + sChartXY = 'chart' + XY, + wh = horiz ? 'width' : 'height', + plotLeftTop = chart['plot' + (horiz ? 'Left' : 'Top')], + selectionWH, + selectionXY, + clipXY, + scale = 1, + inverted = chart.inverted, + bounds = chart.bounds[horiz ? 'h' : 'v'], + singleTouch = pinchDown.length === 1, + touch0Start = pinchDown[0][sChartXY], + touch0Now = touches[0][sChartXY], + touch1Start = !singleTouch && pinchDown[1][sChartXY], + touch1Now = !singleTouch && touches[1][sChartXY], + outOfBounds, + transformScale, + scaleKey, + setScale = function () { + if (!singleTouch && mathAbs(touch0Start - touch1Start) > 20) { // Don't zoom if fingers are too close on this axis + scale = mathAbs(touch0Now - touch1Now) / mathAbs(touch0Start - touch1Start); + } + + clipXY = ((plotLeftTop - touch0Now) / scale) + touch0Start; + selectionWH = chart['plot' + (horiz ? 'Width' : 'Height')] / scale; + }; - removeEvent(doc, hasTouch ? 'touchend' : 'mouseup', drop); - } + // Set the scale, first pass + setScale(); - /** - * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea. - */ - mouseTracker.hideTooltipOnMouseMove = function (e) { + selectionXY = clipXY; // the clip position (x or y) is altered if out of bounds, the selection position is not - // Get e.pageX and e.pageY back in MooTools - washMouseEvent(e); + // Out of bounds + if (selectionXY < bounds.min) { + selectionXY = bounds.min; + outOfBounds = true; + } else if (selectionXY + selectionWH > bounds.max) { + selectionXY = bounds.max - selectionWH; + outOfBounds = true; + } + + // Is the chart dragged off its bounds, determined by dataMin and dataMax? + if (outOfBounds) { - // If we're outside, hide the tooltip - if (mouseTracker.chartPosition && chart.hoverSeries && chart.hoverSeries.isCartesian && - !chart.isInsidePlot(e.pageX - mouseTracker.chartPosition.left - chart.plotLeft, - e.pageY - mouseTracker.chartPosition.top - chart.plotTop)) { - mouseTracker.resetTracker(); + // Modify the touchNow position in order to create an elastic drag movement. This indicates + // to the user that the chart is responsive but can't be dragged further. + touch0Now -= 0.8 * (touch0Now - lastValidTouch[xy][0]); + if (!singleTouch) { + touch1Now -= 0.8 * (touch1Now - lastValidTouch[xy][1]); } - }; - /** - * When mouse leaves the container, hide the tooltip. - */ - mouseTracker.hideTooltipOnMouseLeave = function () { - mouseTracker.resetTracker(); - mouseTracker.chartPosition = null; // also reset the chart position, used in #149 fix - }; + // Set the scale, second pass to adapt to the modified touchNow positions + setScale(); + } else { + lastValidTouch[xy] = [touch0Now, touch1Now]; + } - /* - * Record the starting position of a dragoperation - */ - container.onmousedown = function (e) { - e = mouseTracker.normalizeMouseEvent(e); + + // Set geometry for clipping, selection and transformation + if (!inverted) { // TODO: implement clipping for inverted charts + clip[xy] = clipXY - plotLeftTop; + clip[wh] = selectionWH; + } + scaleKey = inverted ? (horiz ? 'scaleY' : 'scaleX') : 'scale' + XY; + transformScale = inverted ? 1 / scale : scale; + + selectionMarker[wh] = selectionWH; + selectionMarker[xy] = selectionXY; + transform[scaleKey] = scale; + transform['translate' + XY] = (transformScale * plotLeftTop) + (touch0Now - (transformScale * touch0Start)); + }, + + /** + * Handle touch events with two touches + */ + pinch: function (e) { + + var self = this, + chart = self.chart, + pinchDown = self.pinchDown, + followTouchMove = chart.tooltip && chart.tooltip.options.followTouchMove, + touches = e.touches, + touchesLength = touches.length, + lastValidTouch = self.lastValidTouch, + zoomHor = self.zoomHor || self.pinchHor, + zoomVert = self.zoomVert || self.pinchVert, + hasZoom = zoomHor || zoomVert, + selectionMarker = self.selectionMarker, + transform = {}, + clip = {}; - // issue #295, dragging not always working in Firefox - if (!hasTouch && e.preventDefault) { + // On touch devices, only proceed to trigger click if a handler is defined + if (e.type === 'touchstart') { + if (followTouchMove || hasZoom) { e.preventDefault(); } + } + + // Normalize each touch + map(touches, function (e) { + return self.normalize(e); + }); + + // Register the touch start position + if (e.type === 'touchstart') { + each(touches, function (e, i) { + pinchDown[i] = { chartX: e.chartX, chartY: e.chartY }; + }); + lastValidTouch.x = [pinchDown[0].chartX, pinchDown[1] && pinchDown[1].chartX]; + lastValidTouch.y = [pinchDown[0].chartY, pinchDown[1] && pinchDown[1].chartY]; - // record the start position - chart.mouseIsDown = true; - chart.cancelClick = false; - chart.mouseDownX = mouseTracker.mouseDownX = e.chartX; - mouseTracker.mouseDownY = e.chartY; - - addEvent(doc, hasTouch ? 'touchend' : 'mouseup', drop); - }; + // Identify the data bounds in pixels + each(chart.axes, function (axis) { + if (axis.zoomEnabled) { + var bounds = chart.bounds[axis.horiz ? 'h' : 'v'], + minPixelPadding = axis.minPixelPadding, + min = axis.toPixels(axis.dataMin), + max = axis.toPixels(axis.dataMax), + absMin = mathMin(min, max), + absMax = mathMax(min, max); + + // Store the bounds for use in the touchmove handler + bounds.min = mathMin(axis.pos, absMin - minPixelPadding); + bounds.max = mathMax(axis.pos + axis.len, absMax + minPixelPadding); + } + }); + + // Event type is touchmove, handle panning and pinching + } else if (pinchDown.length) { // can be 0 when releasing, if touchend fires first + - // The mousemove, touchmove and touchstart event handler - var mouseMove = function (e) { - // let the system handle multitouch operations like two finger scroll - // and pinching - if (e && e.touches && e.touches.length > 1) { - return; + // Set the marker + if (!selectionMarker) { + self.selectionMarker = selectionMarker = extend({ + destroy: noop + }, chart.plotBox); } - // normalize - e = mouseTracker.normalizeMouseEvent(e); - if (!hasTouch) { // not for touch devices - e.returnValue = false; + + + if (zoomHor) { + self.pinchTranslateDirection(true, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); + } + if (zoomVert) { + self.pinchTranslateDirection(false, pinchDown, touches, transform, selectionMarker, clip, lastValidTouch); } - var chartX = e.chartX, - chartY = e.chartY, - isOutsidePlot = !chart.isInsidePlot(chartX - chart.plotLeft, chartY - chart.plotTop); + self.hasPinched = hasZoom; - // on touch devices, only trigger click if a handler is defined - if (hasTouch && e.type === 'touchstart') { - if (attr(e.target, 'isTracker')) { - if (!chart.runTrackerClick) { - e.preventDefault(); - } - } else if (!chart.runChartClick && !isOutsidePlot) { - e.preventDefault(); - } + // Scale and translate the groups to provide visual feedback during pinching + self.scaleGroups(transform, clip); + + // Optionally move the tooltip on touchmove + if (!hasZoom && followTouchMove && touchesLength === 1) { + this.runPointActions(self.normalize(e)); } + } + }, - // cancel on mouse outside - if (isOutsidePlot) { + /** + * Start a drag operation + */ + dragStart: function (e) { + var chart = this.chart; - /*if (!lastWasOutsidePlot) { - // reset the tracker - resetTracker(); - }*/ + // Record the start position + chart.mouseIsDown = e.type; + chart.cancelClick = false; + chart.mouseDownX = this.mouseDownX = e.chartX; + this.mouseDownY = e.chartY; + }, - // drop the selection if any and reset mouseIsDown and hasDragged - //drop(); - if (chartX < chart.plotLeft) { - chartX = chart.plotLeft; - } else if (chartX > chart.plotLeft + chart.plotWidth) { - chartX = chart.plotLeft + chart.plotWidth; - } + /** + * Perform a drag operation in response to a mousemove event while the mouse is down + */ + drag: function (e) { - if (chartY < chart.plotTop) { - chartY = chart.plotTop; - } else if (chartY > chart.plotTop + chart.plotHeight) { - chartY = chart.plotTop + chart.plotHeight; + var chart = this.chart, + chartOptions = chart.options.chart, + chartX = e.chartX, + chartY = e.chartY, + zoomHor = this.zoomHor, + zoomVert = this.zoomVert, + plotLeft = chart.plotLeft, + plotTop = chart.plotTop, + plotWidth = chart.plotWidth, + plotHeight = chart.plotHeight, + clickedInside, + size, + mouseDownX = this.mouseDownX, + mouseDownY = this.mouseDownY; + + // If the mouse is outside the plot area, adjust to cooordinates + // inside to prevent the selection marker from going outside + if (chartX < plotLeft) { + chartX = plotLeft; + } else if (chartX > plotLeft + plotWidth) { + chartX = plotLeft + plotWidth; + } + + if (chartY < plotTop) { + chartY = plotTop; + } else if (chartY > plotTop + plotHeight) { + chartY = plotTop + plotHeight; + } + + // determine if the mouse has moved more than 10px + this.hasDragged = Math.sqrt( + Math.pow(mouseDownX - chartX, 2) + + Math.pow(mouseDownY - chartY, 2) + ); + if (this.hasDragged > 10) { + clickedInside = chart.isInsidePlot(mouseDownX - plotLeft, mouseDownY - plotTop); + + // make a selection + if (chart.hasCartesianSeries && (this.zoomX || this.zoomY) && clickedInside) { + if (!this.selectionMarker) { + this.selectionMarker = chart.renderer.rect( + plotLeft, + plotTop, + zoomHor ? 1 : plotWidth, + zoomVert ? 1 : plotHeight, + 0 + ) + .attr({ + fill: chartOptions.selectionMarkerFill || 'rgba(69,114,167,0.25)', + zIndex: 7 + }) + .add(); } } - if (chart.mouseIsDown && e.type !== 'touchstart') { // make selection - - // determine if the mouse has moved more than 10px - hasDragged = Math.sqrt( - Math.pow(mouseTracker.mouseDownX - chartX, 2) + - Math.pow(mouseTracker.mouseDownY - chartY, 2) - ); - if (hasDragged > 10) { - var clickedInside = chart.isInsidePlot(mouseTracker.mouseDownX - chart.plotLeft, mouseTracker.mouseDownY - chart.plotTop); + // adjust the width of the selection marker + if (this.selectionMarker && zoomHor) { + size = chartX - mouseDownX; + this.selectionMarker.attr({ + width: mathAbs(size), + x: (size > 0 ? 0 : size) + mouseDownX + }); + } + // adjust the height of the selection marker + if (this.selectionMarker && zoomVert) { + size = chartY - mouseDownY; + this.selectionMarker.attr({ + height: mathAbs(size), + y: (size > 0 ? 0 : size) + mouseDownY + }); + } - // make a selection - if (chart.hasCartesianSeries && (mouseTracker.zoomX || mouseTracker.zoomY) && clickedInside) { - if (!mouseTracker.selectionMarker) { - mouseTracker.selectionMarker = chart.renderer.rect( - chart.plotLeft, - chart.plotTop, - zoomHor ? 1 : chart.plotWidth, - zoomVert ? 1 : chart.plotHeight, - 0 - ) - .attr({ - fill: mouseTracker.options.chart.selectionMarkerFill || 'rgba(69,114,167,0.25)', - zIndex: 7 - }) - .add(); - } - } + // panning + if (clickedInside && !this.selectionMarker && chartOptions.panning) { + chart.pan(chartX); + } + } + }, - // adjust the width of the selection marker - if (mouseTracker.selectionMarker && zoomHor) { - var xSize = chartX - mouseTracker.mouseDownX; - mouseTracker.selectionMarker.attr({ - width: mathAbs(xSize), - x: (xSize > 0 ? 0 : xSize) + mouseTracker.mouseDownX - }); - } - // adjust the height of the selection marker - if (mouseTracker.selectionMarker && zoomVert) { - var ySize = chartY - mouseTracker.mouseDownY; - mouseTracker.selectionMarker.attr({ - height: mathAbs(ySize), - y: (ySize > 0 ? 0 : ySize) + mouseTracker.mouseDownY - }); - } + /** + * On mouse up or touch end across the entire document, drop the selection. + */ + drop: function (e) { + var chart = this.chart, + hasPinched = this.hasPinched; - // panning - if (clickedInside && !mouseTracker.selectionMarker && mouseTracker.options.chart.panning) { - chart.pan(chartX); + if (this.selectionMarker) { + var selectionData = { + xAxis: [], + yAxis: [], + originalEvent: e.originalEvent || e + }, + selectionBox = this.selectionMarker, + selectionLeft = selectionBox.x, + selectionTop = selectionBox.y, + runZoom; + // a selection has been made + if (this.hasDragged || hasPinched) { + + // record each axis' min and max + each(chart.axes, function (axis) { + if (axis.zoomEnabled) { + var horiz = axis.horiz, + selectionMin = axis.toValue((horiz ? selectionLeft : selectionTop)), + selectionMax = axis.toValue((horiz ? selectionLeft + selectionBox.width : selectionTop + selectionBox.height)); + + if (!isNaN(selectionMin) && !isNaN(selectionMax)) { // #859 + selectionData[axis.xOrY + 'Axis'].push({ + axis: axis, + min: mathMin(selectionMin, selectionMax), // for reversed axes, + max: mathMax(selectionMin, selectionMax) + }); + runZoom = true; + } } + }); + if (runZoom) { + fireEvent(chart, 'selection', selectionData, function (args) { + chart.zoom(extend(args, hasPinched ? { animation: false } : null)); + }); } - } else if (!isOutsidePlot) { - // show the tooltip - mouseTracker.onmousemove(e); } + this.selectionMarker = this.selectionMarker.destroy(); - lastWasOutsidePlot = isOutsidePlot; + // Reset scaling preview + if (hasPinched) { + this.scaleGroups({ + translateX: chart.plotLeft, + translateY: chart.plotTop, + scaleX: 1, + scaleY: 1 + }); + } + } - // when outside plot, allow touch-drag by returning true - return isOutsidePlot || !chart.hasCartesianSeries; - }; + // Reset all + if (chart) { // it may be destroyed on mouse up - #877 + css(chart.container, { cursor: chart._cursor }); + chart.cancelClick = this.hasDragged > 10; // #370 + chart.mouseIsDown = this.hasDragged = this.hasPinched = false; + this.pinchDown = []; + } + }, - /* - * When the mouse enters the container, run mouseMove - */ - container.onmousemove = mouseMove; + onContainerMouseDown: function (e) { - /* - * When the mouse leaves the container, hide the tracking (tooltip). - */ - addEvent(container, 'mouseleave', mouseTracker.hideTooltipOnMouseLeave); + e = this.normalize(e); - // issue #149 workaround - // The mouseleave event above does not always fire. Whenever the mouse is moving - // outside the plotarea, hide the tooltip - addEvent(doc, 'mousemove', mouseTracker.hideTooltipOnMouseMove); - - container.ontouchstart = function (e) { - // For touch devices, use touchmove to zoom - if (mouseTracker.zoomX || mouseTracker.zoomY) { - container.onmousedown(e); - } - // Show tooltip and prevent the lower mouse pseudo event - mouseMove(e); - }; + // issue #295, dragging not always working in Firefox + if (e.preventDefault) { + e.preventDefault(); + } + + this.dragStart(e); + }, - /* - * Allow dragging the finger over the chart to read the values on touch - * devices - */ - container.ontouchmove = mouseMove; + - /* - * Allow dragging the finger over the chart to read the values on touch - * devices - */ - container.ontouchend = function () { - if (hasDragged) { - mouseTracker.resetTracker(); - } - }; + onDocumentMouseUp: function (e) { + this.drop(e); + }, + /** + * Special handler for mouse move that will hide the tooltip when the mouse leaves the plotarea. + * Issue #149 workaround. The mouseleave event does not always fire. + */ + onDocumentMouseMove: function (e) { + var chart = this.chart, + chartPosition = this.chartPosition, + hoverSeries = chart.hoverSeries; - // MooTools 1.2.3 doesn't fire this in IE when using addEvent - container.onclick = function (e) { - var hoverPoint = chart.hoverPoint, - plotX, - plotY; - e = mouseTracker.normalizeMouseEvent(e); + // Get e.pageX and e.pageY back in MooTools + e = washMouseEvent(e); + + // If we're outside, hide the tooltip + if (chartPosition && hoverSeries && hoverSeries.isCartesian && + !chart.isInsidePlot(e.pageX - chartPosition.left - chart.plotLeft, + e.pageY - chartPosition.top - chart.plotTop)) { + this.reset(); + } + }, - e.cancelBubble = true; // IE specific + /** + * When mouse leaves the container, hide the tooltip. + */ + onContainerMouseLeave: function () { + this.reset(); + this.chartPosition = null; // also reset the chart position, used in #149 fix + }, + // The mousemove, touchmove and touchstart event handler + onContainerMouseMove: function (e) { - if (!chart.cancelClick) { - // Detect clicks on trackers or tracker groups, #783 - if (hoverPoint && (attr(e.target, 'isTracker') || attr(e.target.parentNode, 'isTracker'))) { - plotX = hoverPoint.plotX; - plotY = hoverPoint.plotY; - - // add page position info - extend(hoverPoint, { - pageX: mouseTracker.chartPosition.left + chart.plotLeft + - (chart.inverted ? chart.plotWidth - plotY : plotX), - pageY: mouseTracker.chartPosition.top + chart.plotTop + - (chart.inverted ? chart.plotHeight - plotX : plotY) - }); + var chart = this.chart; + + // normalize + e = this.normalize(e); + + // #295 + e.returnValue = false; + + + if (chart.mouseIsDown === 'mousedown') { + this.drag(e); + } + + // Show the tooltip and run mouse over events (#977) + if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop) && !chart.openMenu) { + this.runPointActions(e); + } + }, + + /** + * Utility to detect whether an element has, or has a parent with, a specific + * class name. Used on detection of tracker objects and on deciding whether + * hovering the tooltip should cause the active series to mouse out. + */ + inClass: function (element, className) { + var elemClassName; + while (element) { + elemClassName = attr(element, 'class'); + if (elemClassName) { + if (elemClassName.indexOf(className) !== -1) { + return true; + } else if (elemClassName.indexOf(PREFIX + 'container') !== -1) { + return false; + } + } + element = element.parentNode; + } + }, + + onTrackerMouseOut: function (e) { + var series = this.chart.hoverSeries; + if (series && !series.options.stickyTracking && !this.inClass(e.toElement || e.relatedTarget, PREFIX + 'tooltip')) { + series.onMouseOut(); + } + }, - // the series click event - fireEvent(hoverPoint.series, 'click', extend(e, { - point: hoverPoint - })); + onContainerClick: function (e) { + var chart = this.chart, + hoverPoint = chart.hoverPoint, + plotLeft = chart.plotLeft, + plotTop = chart.plotTop, + inverted = chart.inverted, + chartPosition, + plotX, + plotY; + + e = this.normalize(e); + e.cancelBubble = true; // IE specific - // the point click event + if (!chart.cancelClick) { + + // On tracker click, fire the series and point events. #783, #1583 + if (hoverPoint && this.inClass(e.target, PREFIX + 'tracker')) { + chartPosition = this.chartPosition; + plotX = hoverPoint.plotX; + plotY = hoverPoint.plotY; + + // add page position info + extend(hoverPoint, { + pageX: chartPosition.left + plotLeft + + (inverted ? chart.plotWidth - plotY : plotX), + pageY: chartPosition.top + plotTop + + (inverted ? chart.plotHeight - plotX : plotY) + }); + + // the series click event + fireEvent(hoverPoint.series, 'click', extend(e, { + point: hoverPoint + })); + + // the point click event + if (chart.hoverPoint) { // it may be destroyed (#1844) hoverPoint.firePointEvent('click', e); + } - } else { - extend(e, mouseTracker.getMouseCoordinates(e)); + // When clicking outside a tracker, fire a chart event + } else { + extend(e, this.getCoordinates(e)); - // fire a click event in the chart - if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { - fireEvent(chart, 'click', e); - } + // fire a click event in the chart + if (chart.isInsidePlot(e.chartX - plotLeft, e.chartY - plotTop)) { + fireEvent(chart, 'click', e); } + } + + + } + }, + + onContainerTouchStart: function (e) { + var chart = this.chart; + + if (e.touches.length === 1) { + e = this.normalize(e); + if (chart.isInsidePlot(e.chartX - chart.plotLeft, e.chartY - chart.plotTop)) { + + // Prevent the click pseudo event from firing unless it is set in the options + /*if (!chart.runChartClick) { + e.preventDefault(); + }*/ + + // Run mouse events and display tooltip etc + this.runPointActions(e); + + this.pinch(e); } - }; + } else if (e.touches.length === 2) { + this.pinch(e); + } + }, + + onContainerTouchMove: function (e) { + if (e.touches.length === 1 || e.touches.length === 2) { + this.pinch(e); + } + }, + + onDocumentTouchEnd: function (e) { + this.drop(e); }, /** - * Destroys the MouseTracker object and disconnects DOM events. + * Set the JS DOM events on the container and document. This method should contain + * a one-to-one assignment between methods and their handlers. Any advanced logic should + * be moved to the handler reflecting the event's name. */ - destroy: function () { - var mouseTracker = this, - chart = mouseTracker.chart, - container = chart.container; - - // Destroy the tracker group element - if (chart.trackerGroup) { - chart.trackerGroup = chart.trackerGroup.destroy(); + setDOMEvents: function () { + + var pointer = this, + container = pointer.chart.container, + events; + + this._events = events = [ + [container, 'onmousedown', 'onContainerMouseDown'], + [container, 'onmousemove', 'onContainerMouseMove'], + [container, 'onclick', 'onContainerClick'], + [container, 'mouseleave', 'onContainerMouseLeave'], + [doc, 'mousemove', 'onDocumentMouseMove'], + [doc, 'mouseup', 'onDocumentMouseUp'] + ]; + + if (hasTouch) { + events.push( + [container, 'ontouchstart', 'onContainerTouchStart'], + [container, 'ontouchmove', 'onContainerTouchMove'], + [doc, 'touchend', 'onDocumentTouchEnd'] + ); } - removeEvent(container, 'mouseleave', mouseTracker.hideTooltipOnMouseLeave); - removeEvent(doc, 'mousemove', mouseTracker.hideTooltipOnMouseMove); - container.onclick = container.onmousedown = container.onmousemove = container.ontouchstart = container.ontouchend = container.ontouchmove = null; + each(events, function (eventConfig) { - // memory and CPU leak - clearInterval(this.tooltipInterval); - }, + // First, create the callback function that in turn calls the method on Pointer + pointer['_' + eventConfig[2]] = function (e) { + pointer[eventConfig[2]](e); + }; - // Run MouseTracker - init: function (chart, options) { - if (!chart.trackerGroup) { - chart.trackerGroup = chart.renderer.g('tracker') - .attr({ zIndex: 9 }) - .add(); - } + // Now attach the function, either as a direct property or through addEvent + if (eventConfig[1].indexOf('on') === 0) { + eventConfig[0][eventConfig[1]] = pointer['_' + eventConfig[2]]; + } else { + addEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]); + } + }); - if (options.enabled) { - chart.tooltip = new Tooltip(chart, options); + + }, - // set the fixed interval ticking for the smooth tooltip - this.tooltipInterval = setInterval(function () { chart.tooltip.tick(); }, 32); - } + /** + * Destroys the Pointer object and disconnects DOM events. + */ + destroy: function () { + var pointer = this; - this.setDOMEvents(); + // Release all DOM events + each(pointer._events, function (eventConfig) { + if (eventConfig[1].indexOf('on') === 0) { + eventConfig[0][eventConfig[1]] = null; // delete breaks oldIE + } else { + removeEvent(eventConfig[0], eventConfig[1], pointer['_' + eventConfig[2]]); + } + }); + delete pointer._events; + + // memory and CPU leak + clearInterval(pointer.tooltipTimeout); } }; /** * The overview of the chart's series */ -function Legend(chart) { - - this.init(chart); +function Legend(chart, options) { + this.init(chart, options); } Legend.prototype = { @@ -8725,19 +9707,19 @@ /** * Initialize the legend */ - init: function (chart) { + init: function (chart, options) { + var legend = this, - options = legend.options = chart.options.legend; + itemStyle = options.itemStyle, + padding = pick(options.padding, 8), + itemMarginTop = options.itemMarginTop || 0; + this.options = options; + if (!options.enabled) { return; } - var //style = options.style || {}, // deprecated - itemStyle = options.itemStyle, - padding = pick(options.padding, 8), - itemMarginTop = options.itemMarginTop || 0; - legend.baseline = pInt(itemStyle.fontSize) + 3 + itemMarginTop; // used in Series prototype legend.itemStyle = itemStyle; legend.itemHiddenStyle = merge(itemStyle, options.itemHiddenStyle); @@ -8747,35 +9729,17 @@ legend.initialItemY = padding - 5; // 5 is the number of pixels above the text legend.maxItemWidth = 0; legend.chart = chart; - //legend.allItems = UNDEFINED; - //legend.legendWidth = UNDEFINED; - //legend.legendHeight = UNDEFINED; - //legend.offsetWidth = UNDEFINED; legend.itemHeight = 0; legend.lastLineHeight = 0; - //legend.itemX = UNDEFINED; - //legend.itemY = UNDEFINED; - //legend.lastItemY = UNDEFINED; - - // Elements - //legend.group = UNDEFINED; - //legend.box = UNDEFINED; - // run legend + // Render it legend.render(); // move checkboxes - addEvent(legend.chart, 'endResize', function () { legend.positionCheckboxes(); }); + addEvent(legend.chart, 'endResize', function () { + legend.positionCheckboxes(); + }); -/* // expose - return { - colorizeItem: colorizeItem, - destroyItem: destroyItem, - render: render, - destroy: destroy, - getLegendWidth: getLegendWidth, - getLegendHeight: getLegendHeight - };*/ }, /** @@ -8791,19 +9755,37 @@ legendSymbol = item.legendSymbol, hiddenColor = legend.itemHiddenStyle.color, textColor = visible ? options.itemStyle.color : hiddenColor, - symbolColor = visible ? item.color : hiddenColor; + symbolColor = visible ? item.color : hiddenColor, + markerOptions = item.options && item.options.marker, + symbolAttr = { + stroke: symbolColor, + fill: symbolColor + }, + key, + val; + if (legendItem) { - legendItem.css({ fill: textColor }); + legendItem.css({ fill: textColor, color: textColor }); // color for #1553, oldIE } if (legendLine) { legendLine.attr({ stroke: symbolColor }); } + if (legendSymbol) { - legendSymbol.attr({ - stroke: symbolColor, - fill: symbolColor - }); + + // Apply marker options + if (markerOptions) { + markerOptions = item.convertAttribs(markerOptions); + for (key in markerOptions) { + val = markerOptions[key]; + if (val !== UNDEFINED) { + symbolAttr[key] = val; + } + } + } + + legendSymbol.attr(symbolAttr); } }, @@ -8873,19 +9855,49 @@ /** * Position the checkboxes after the width is determined */ - positionCheckboxes: function () { - var legend = this; - - each(legend.allItems, function (item) { - var checkbox = item.checkbox, - alignAttr = legend.group.alignAttr; - if (checkbox) { - css(checkbox, { - left: (alignAttr.translateX + item.legendItemWidth + checkbox.x - 20) + PX, - top: (alignAttr.translateY + checkbox.y + 3) + PX - }); + positionCheckboxes: function (scrollOffset) { + var alignAttr = this.group.alignAttr, + translateY, + clipHeight = this.clipHeight || this.legendHeight; + + if (alignAttr) { + translateY = alignAttr.translateY; + each(this.allItems, function (item) { + var checkbox = item.checkbox, + top; + + if (checkbox) { + top = (translateY + checkbox.y + (scrollOffset || 0) + 3); + css(checkbox, { + left: (alignAttr.translateX + item.legendItemWidth + checkbox.x - 20) + PX, + top: top + PX, + display: top > translateY - 6 && top < translateY + clipHeight - 6 ? '' : NONE + }); + } + }); + } + }, + + /** + * Render the legend title on top of the legend + */ + renderTitle: function () { + var options = this.options, + padding = this.padding, + titleOptions = options.title, + titleHeight = 0; + + if (titleOptions.text) { + if (!this.title) { + this.title = this.chart.renderer.label(titleOptions.text, padding - 3, padding - 4, null, null, null, null, null, 'legend-title') + .attr({ zIndex: 1 }) + .css(titleOptions.style) + .add(this.group); } - }); + titleHeight = this.title.getBBox().height; + this.contentGroup.attr({ translateY: titleHeight }); + } + this.titleHeight = titleHeight; }, /** @@ -8914,7 +9926,8 @@ li = item.legendItem, series = item.series || item, itemOptions = series.options, - showCheckbox = itemOptions.showCheckbox; + showCheckbox = itemOptions.showCheckbox, + useHTML = options.useHTML; if (!li) { // generate it once, later move it @@ -8929,10 +9942,10 @@ // Generate the list item text and add it to the group item.legendItem = li = renderer.text( - options.labelFormatter.call(item), + options.labelFormat ? format(options.labelFormat, item) : options.labelFormatter.call(item), ltr ? symbolWidth + symbolPadding : -symbolPadding, legend.baseline, - options.useHTML + useHTML ) .css(merge(item.visible ? itemStyle : itemHiddenStyle)) // merge to prevent modifying original (#1021) .attr({ @@ -8941,8 +9954,8 @@ }) .add(item.legendGroup); - // Set the events on the item group - item.legendGroup.on('mouseover', function () { + // Set the events on the item group, or in case of useHTML, the item itself (#1249) + (useHTML ? li : item.legendGroup).on('mouseover', function () { item.setState(HOVER_STATE); li.css(legend.options.itemHoverStyle); }) @@ -9067,9 +10080,6 @@ if (!legendGroup) { legend.group = legendGroup = renderer.g('legend') - // #414, #759. Trackers will be drawn above the legend, but we have - // to sacrifice that because tooltips need to be above the legend - // and trackers above tooltips .attr({ zIndex: 7 }) .add(); legend.contentGroup = renderer.g() @@ -9077,16 +10087,16 @@ .add(legendGroup); legend.scrollGroup = renderer.g() .add(legend.contentGroup); - legend.clipRect = renderer.clipRect(0, 0, 9999, chart.chartHeight); - legend.contentGroup.clip(legend.clipRect); } + + legend.renderTitle(); // add each series or point allItems = []; each(chart.series, function (serie) { var seriesOptions = serie.options; - if (!seriesOptions.showInLegend) { + if (!seriesOptions.showInLegend || defined(seriesOptions.linkedTo)) { return; } @@ -9101,7 +10111,7 @@ // sort by legendIndex stableSort(allItems, function (a, b) { - return (a.options.legendIndex || 0) - (b.options.legendIndex || 0); + return ((a.options && a.options.legendIndex) || 0) - ((b.options && b.options.legendIndex) || 0); }); // reversed legend @@ -9119,7 +10129,7 @@ // Draw the border legendWidth = options.width || legend.offsetWidth; - legendHeight = legend.lastItemY + legend.lastLineHeight; + legendHeight = legend.lastItemY + legend.lastLineHeight + legend.titleHeight; legendHeight = legend.handleOverflow(legendHeight); @@ -9181,7 +10191,7 @@ legendGroup.align(extend({ width: legendWidth, height: legendHeight - }, options), true, chart.spacingBox); + }, options), true, 'spacingBox'); } if (!chart.isResizing) { @@ -9202,7 +10212,7 @@ optionsY = options.y, alignTop = options.verticalAlign === 'top', spaceHeight = chart.spacingBox.height + (alignTop ? -optionsY : optionsY) - this.padding, - maxHeight = options.maxHeight, // docs + maxHeight = options.maxHeight, clipHeight, clipRect = this.clipRect, navOptions = options.navigation, @@ -9219,13 +10229,18 @@ } // Reset the legend height and adjust the clipping rectangle - if (legendHeight > spaceHeight) { - - this.clipHeight = clipHeight = spaceHeight - 20; + if (legendHeight > spaceHeight && !options.useHTML) { + + this.clipHeight = clipHeight = spaceHeight - 20 - this.titleHeight; this.pageCount = pageCount = mathCeil(legendHeight / clipHeight); this.currentPage = pick(this.currentPage, 1); this.fullHeight = legendHeight; + // Only apply clipping if needed. Clipping causes blurred legend in PDF export (#1787) + if (!clipRect) { + clipRect = legend.clipRect = renderer.clipRect(0, 0, 9999, 0); + legend.contentGroup.clip(clipRect); + } clipRect.attr({ height: clipHeight }); @@ -9261,6 +10276,7 @@ this.scrollGroup.attr({ translateY: 1 }); + this.clipHeight = 0; // #1379 } return legendHeight; @@ -9279,7 +10295,8 @@ activeColor = navOptions.activeColor, inactiveColor = navOptions.inactiveColor, pager = this.pager, - padding = this.padding; + padding = this.padding, + scrollOffset; // When resizing while looking at the last page if (currentPage > pageCount) { @@ -9294,7 +10311,7 @@ this.nav.attr({ translateX: padding, - translateY: clipHeight + 7, + translateY: clipHeight + 7 + this.titleHeight, visibility: VISIBLE }); this.up.attr({ @@ -9314,8 +10331,9 @@ cursor: currentPage === pageCount ? 'default' : 'pointer' }); + scrollOffset = -mathMin(clipHeight * (currentPage - 1), this.fullHeight - clipHeight + padding) + 1; this.scrollGroup.animate({ - translateY: -mathMin(clipHeight * (currentPage - 1), this.fullHeight - clipHeight + padding) + 1 + translateY: scrollOffset }); pager.attr({ text: currentPage + '/' + pageCount @@ -9323,93 +10341,139 @@ this.currentPage = currentPage; + this.positionCheckboxes(scrollOffset); } } }; - /** * The chart class * @param {Object} options * @param {Function} callback Function to run when the chart has loaded */ -function Chart(userOptions, callback) { - // Handle regular options - var options, - seriesOptions = userOptions.series; // skip merging data points to increase performance - userOptions.series = null; - options = merge(defaultOptions, userOptions); // do the merge - options.series = userOptions.series = seriesOptions; // set back the series data - - var optionsChart = options.chart, - optionsMargin = optionsChart.margin, - margin = isObject(optionsMargin) ? - optionsMargin : - [optionsMargin, optionsMargin, optionsMargin, optionsMargin]; - - this.optionsMarginTop = pick(optionsChart.marginTop, margin[0]); - this.optionsMarginRight = pick(optionsChart.marginRight, margin[1]); - this.optionsMarginBottom = pick(optionsChart.marginBottom, margin[2]); - this.optionsMarginLeft = pick(optionsChart.marginLeft, margin[3]); - - var chartEvents = optionsChart.events; - - this.runChartClick = chartEvents && !!chartEvents.click; - this.callback = callback; - this.isResizing = 0; - this.options = options; - //chartTitleOptions = UNDEFINED; - //chartSubtitleOptions = UNDEFINED; - - this.axes = []; - this.series = []; - this.hasCartesianSeries = optionsChart.showAxes; - //this.axisOffset = UNDEFINED; - //this.maxTicks = UNDEFINED; // handle the greatest amount of ticks on grouped axes - //this.inverted = UNDEFINED; - //this.loadingShown = UNDEFINED; - //this.container = UNDEFINED; - //this.chartWidth = UNDEFINED; - //this.chartHeight = UNDEFINED; - //this.marginRight = UNDEFINED; - //this.marginBottom = UNDEFINED; - //this.containerWidth = UNDEFINED; - //this.containerHeight = UNDEFINED; - //this.oldChartWidth = UNDEFINED; - //this.oldChartHeight = UNDEFINED; - - //this.renderTo = UNDEFINED; - //this.renderToClone = UNDEFINED; - //this.tracker = UNDEFINED; - - //this.spacingBox = UNDEFINED - - //this.legend = UNDEFINED; - - // Elements - //this.chartBackground = UNDEFINED; - //this.plotBackground = UNDEFINED; - //this.plotBGImage = UNDEFINED; - //this.plotBorder = UNDEFINED; - //this.loadingDiv = UNDEFINED; - //this.loadingSpan = UNDEFINED; - - this.init(chartEvents); +function Chart() { + this.init.apply(this, arguments); } Chart.prototype = { /** + * Initialize the chart + */ + init: function (userOptions, callback) { + + // Handle regular options + var options, + seriesOptions = userOptions.series; // skip merging data points to increase performance + + userOptions.series = null; + options = merge(defaultOptions, userOptions); // do the merge + options.series = userOptions.series = seriesOptions; // set back the series data + + var optionsChart = options.chart, + optionsMargin = optionsChart.margin, + margin = isObject(optionsMargin) ? + optionsMargin : + [optionsMargin, optionsMargin, optionsMargin, optionsMargin]; + + this.optionsMarginTop = pick(optionsChart.marginTop, margin[0]); + this.optionsMarginRight = pick(optionsChart.marginRight, margin[1]); + this.optionsMarginBottom = pick(optionsChart.marginBottom, margin[2]); + this.optionsMarginLeft = pick(optionsChart.marginLeft, margin[3]); + + var chartEvents = optionsChart.events; + + this.runChartClick = chartEvents && !!chartEvents.click; + this.bounds = { h: {}, v: {} }; // Pixel data bounds for touch zoom + + this.callback = callback; + this.isResizing = 0; + this.options = options; + //chartTitleOptions = UNDEFINED; + //chartSubtitleOptions = UNDEFINED; + + this.axes = []; + this.series = []; + this.hasCartesianSeries = optionsChart.showAxes; + //this.axisOffset = UNDEFINED; + //this.maxTicks = UNDEFINED; // handle the greatest amount of ticks on grouped axes + //this.inverted = UNDEFINED; + //this.loadingShown = UNDEFINED; + //this.container = UNDEFINED; + //this.chartWidth = UNDEFINED; + //this.chartHeight = UNDEFINED; + //this.marginRight = UNDEFINED; + //this.marginBottom = UNDEFINED; + //this.containerWidth = UNDEFINED; + //this.containerHeight = UNDEFINED; + //this.oldChartWidth = UNDEFINED; + //this.oldChartHeight = UNDEFINED; + + //this.renderTo = UNDEFINED; + //this.renderToClone = UNDEFINED; + + //this.spacingBox = UNDEFINED + + //this.legend = UNDEFINED; + + // Elements + //this.chartBackground = UNDEFINED; + //this.plotBackground = UNDEFINED; + //this.plotBGImage = UNDEFINED; + //this.plotBorder = UNDEFINED; + //this.loadingDiv = UNDEFINED; + //this.loadingSpan = UNDEFINED; + + var chart = this, + eventType; + + // Add the chart to the global lookup + chart.index = charts.length; + charts.push(chart); + + // Set up auto resize + if (optionsChart.reflow !== false) { + addEvent(chart, 'load', function () { + chart.initReflow(); + }); + } + + // Chart event handlers + if (chartEvents) { + for (eventType in chartEvents) { + addEvent(chart, eventType, chartEvents[eventType]); + } + } + + chart.xAxis = []; + chart.yAxis = []; + + // Expose methods and variables + chart.animation = useCanVG ? false : pick(optionsChart.animation, true); + chart.pointCount = 0; + chart.counters = new ChartCounters(); + + chart.firstRender(); + }, + + /** * Initialize an individual series, called internally before render time */ initSeries: function (options) { var chart = this, optionsChart = chart.options.chart, type = options.type || optionsChart.type || optionsChart.defaultSeriesType, - series = new seriesTypes[type](); + series, + constr = seriesTypes[type]; + + // No such series type + if (!constr) { + error(17, true); + } + series = new constr(); series.init(this, options); return series; }, @@ -9429,17 +10493,14 @@ chart = this; if (options) { - setAnimation(animation, chart); redraw = pick(redraw, true); // defaults to true fireEvent(chart, 'addSeries', { options: options }, function () { - chart.initSeries(options); - //series = chart.initSeries(options); - //series.isDirty = true; - + series = chart.initSeries(options); + chart.isDirtyLegend = true; // the series array is out of sync with the display if (redraw) { - chart.redraw(); + chart.redraw(animation); } }); } @@ -9448,12 +10509,41 @@ }, /** + * Add an axis to the chart + * @param {Object} options The axis option + * @param {Boolean} isX Whether it is an X axis or a value axis + */ + addAxis: function (options, isX, redraw, animation) { + var key = isX ? 'xAxis' : 'yAxis', + chartOptions = this.options, + axis; + + /*jslint unused: false*/ + axis = new Axis(this, merge(options, { + index: this[key].length + })); + /*jslint unused: true*/ + + // Push the new axis options to the chart options + chartOptions[key] = splat(chartOptions[key] || {}); + chartOptions[key].push(options); + + if (pick(redraw, true)) { + this.redraw(animation); + } + }, + + /** * Check whether a given point is within the plot area * - * @param {Number} x Pixel x relative to the plot area - * @param {Number} y Pixel y relative to the plot area - */ - isInsidePlot: function (x, y) { + * @param {Number} plotX Pixel x relative to the plot area + * @param {Number} plotY Pixel y relative to the plot area + * @param {Boolean} inverted Whether the chart is inverted + */ + isInsidePlot: function (plotX, plotY, inverted) { + var x = inverted ? plotY : plotX, + y = inverted ? plotX : plotY; + return x >= 0 && x <= this.plotWidth && y >= 0 && @@ -9482,17 +10572,17 @@ var chart = this, axes = chart.axes, series = chart.series, - tracker = chart.tracker, + pointer = chart.pointer, legend = chart.legend, redrawLegend = chart.isDirtyLegend, hasStackedSeries, isDirtyBox = chart.isDirtyBox, // todo: check if it has actually changed? seriesLength = series.length, i = seriesLength, - clipRect = chart.clipRect, serie, renderer = chart.renderer, - isHiddenChart = renderer.isHidden(); + isHiddenChart = renderer.isHidden(), + afterRedraw = []; setAnimation(animation, chart); @@ -9556,7 +10646,9 @@ // Fire 'afterSetExtremes' only if extremes are set if (axis.isDirtyExtremes) { // #821 axis.isDirtyExtremes = false; - fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751 + afterRedraw.push(function () { // prevent a recursive call to chart.redraw() (#1119) + fireEvent(axis, 'afterSetExtremes', axis.getExtremes()); // #747, #751 + }); } if (axis.isDirty || isDirtyBox || hasStackedSeries) { @@ -9567,23 +10659,13 @@ } - // the plot areas size has changed if (isDirtyBox) { chart.drawChartBox(); - - // move clip rect - if (clipRect) { - stop(clipRect); - clipRect.animate({ // for chart resize - width: chart.plotSizeX, - height: chart.plotSizeY + 1 - }); - } - } + // redraw affected series each(series, function (serie) { if (serie.isDirty && serie.visible && @@ -9592,10 +10674,9 @@ } }); - // move tooltip or reset - if (tracker && tracker.resetTracker) { - tracker.resetTracker(true); + if (pointer && pointer.reset) { + pointer.reset(true); } // redraw if canvas @@ -9607,6 +10688,11 @@ if (isHiddenChart) { chart.cloneRenderTo(true); } + + // Fire callbacks that are put on hold until after the redraw + each(afterRedraw, function (callback) { + callback.call(); + }); }, @@ -9627,10 +10713,6 @@ chart.loadingDiv = loadingDiv = createElement(DIV, { className: PREFIX + 'loading' }, extend(loadingOptions.style, { - left: chart.plotLeft + PX, - top: chart.plotTop + PX, - width: chart.plotWidth + PX, - height: chart.plotHeight + PX, zIndex: 10, display: NONE }), chart.container); @@ -9649,7 +10731,14 @@ // show it if (!chart.loadingShown) { - css(loadingDiv, { opacity: 0, display: '' }); + css(loadingDiv, { + opacity: 0, + display: '', + left: chart.plotLeft + PX, + top: chart.plotTop + PX, + width: chart.plotWidth + PX, + height: chart.plotHeight + PX + }); animate(loadingDiv, { opacity: loadingOptions.style.opacity }, { @@ -9723,21 +10812,18 @@ */ getAxes: function () { var chart = this, - options = this.options; - - var xAxisOptions = options.xAxis || {}, - yAxisOptions = options.yAxis || {}, + options = this.options, + xAxisOptions = options.xAxis = splat(options.xAxis || {}), + yAxisOptions = options.yAxis = splat(options.yAxis || {}), optionsArray, axis; // make sure the options are arrays and add some members - xAxisOptions = splat(xAxisOptions); each(xAxisOptions, function (axis, i) { axis.index = i; axis.isX = true; }); - yAxisOptions = splat(yAxisOptions); each(yAxisOptions, function (axis, i) { axis.index = i; }); @@ -9759,7 +10845,7 @@ getSelectedPoints: function () { var points = []; each(this.series, function (serie) { - points = points.concat(grep(serie.points, function (point) { + points = points.concat(grep(serie.points || [], function (point) { return point.selected; })); }); @@ -9784,32 +10870,26 @@ btnOptions = chart.options.chart.resetZoomButton, theme = btnOptions.theme, states = theme.states, - box = btnOptions.relativeTo === 'chart' ? null : { - x: chart.plotLeft, - y: chart.plotTop, - width: chart.plotWidth, - height: chart.plotHeight - }; + alignTo = btnOptions.relativeTo === 'chart' ? null : 'plotBox'; + this.resetZoomButton = chart.renderer.button(lang.resetZoom, null, null, function () { chart.zoomOut(); }, theme, states && states.hover) .attr({ align: btnOptions.position.align, title: lang.resetZoomTitle }) .add() - .align(btnOptions.position, false, box); + .align(btnOptions.position, false, alignTo); + }, /** * Zoom out to 1:1 */ zoomOut: function () { - var chart = this, - resetZoomButton = chart.resetZoomButton; - - fireEvent(chart, 'selection', { resetSelection: true }, function () { chart.zoom(); }); - if (resetZoomButton) { - chart.resetZoomButton = resetZoomButton.destroy(); - } + var chart = this; + fireEvent(chart, 'selection', { resetSelection: true }, function () { + chart.zoom(); + }); }, /** @@ -9818,39 +10898,44 @@ */ zoom: function (event) { var chart = this, - optionsChart = chart.options.chart; + hasZoomed, + pointer = chart.pointer, + displayButton = false, + resetZoomButton; - // add button to reset selection - var hasZoomed; - - if (chart.resetZoomEnabled !== false && !chart.resetZoomButton) { // hook for Stock charts etc. - chart.showResetZoom(); - } - - // if zoom is called with no arguments, reset the axes + // If zoom is called with no arguments, reset the axes if (!event || event.resetSelection) { each(chart.axes, function (axis) { - if (axis.options.zoomEnabled !== false) { - axis.setExtremes(null, null, false); - hasZoomed = true; - } + hasZoomed = axis.zoom(); }); } else { // else, zoom in on all axes each(event.xAxis.concat(event.yAxis), function (axisData) { - var axis = axisData.axis; + var axis = axisData.axis, + isXAxis = axis.isXAxis; // don't zoom more than minRange - if (chart.tracker[axis.isXAxis ? 'zoomX' : 'zoomY']) { - axis.setExtremes(axisData.min, axisData.max, false); - hasZoomed = true; + if (pointer[isXAxis ? 'zoomX' : 'zoomY'] || pointer[isXAxis ? 'pinchX' : 'pinchY']) { + hasZoomed = axis.zoom(axisData.min, axisData.max); + if (axis.displayBtn) { + displayButton = true; + } } }); } + + // Show or hide the Reset zoom button + resetZoomButton = chart.resetZoomButton; + if (displayButton && !resetZoomButton) { + chart.showResetZoom(); + } else if (!displayButton && isObject(resetZoomButton)) { + chart.resetZoomButton = resetZoomButton.destroy(); + } + // Redraw if (hasZoomed) { chart.redraw( - pick(optionsChart.animation, chart.pointCount < 100) // animation + pick(chart.options.chart.animation, event && event.animation, chart.pointCount < 100) // animation ); } }, @@ -9861,9 +10946,8 @@ * the first chartX position in the dragging operation. */ pan: function (chartX) { - var chart = this; - - var xAxis = chart.xAxis[0], + var chart = this, + xAxis = chart.xAxis[0], mouseDownX = chart.mouseDownX, halfPointRange = xAxis.pointRange / 2, extremes = xAxis.getExtremes(), @@ -9879,7 +10963,7 @@ } if (xAxis.series.length && newMin > mathMin(extremes.dataMin, extremes.min) && newMax < mathMax(extremes.dataMax, extremes.max)) { - xAxis.setExtremes(newMin, newMax, true, false); + xAxis.setExtremes(newMin, newMax, true, false, { trigger: 'pan' }); } chart.mouseDownX = chartX; // set new reference for next run @@ -9899,8 +10983,8 @@ chartTitleOptions, chartSubtitleOptions; - chart.chartTitleOptions = chartTitleOptions = merge(options.title, titleOptions); - chart.chartSubtitleOptions = chartSubtitleOptions = merge(options.subtitle, subtitleOptions); + chartTitleOptions = options.title = merge(options.title, titleOptions); + chartSubtitleOptions = options.subtitle = merge(options.subtitle, subtitleOptions); // add title and subtitle each([ @@ -9913,8 +10997,9 @@ chartTitleOptions = arr[2]; if (title && titleOptions) { - title = title.destroy(); // remove old + chart[name] = title = title.destroy(); // remove old } + if (chartTitleOptions && chartTitleOptions.text && !title) { chart[name] = chart.renderer.text( chartTitleOptions.text, @@ -9929,7 +11014,7 @@ }) .css(chartTitleOptions.style) .add() - .align(chartTitleOptions, false, chart.spacingBox); + .align(chartTitleOptions, false, 'spacingBox'); } }); @@ -9947,10 +11032,10 @@ chart.containerWidth = adapterRun(renderTo, 'width'); chart.containerHeight = adapterRun(renderTo, 'height'); - chart.chartWidth = optionsChart.width || chart.containerWidth || 600; - chart.chartHeight = optionsChart.height || + chart.chartWidth = mathMax(0, optionsChart.width || chart.containerWidth || 600); // #1393, 1460 + chart.chartHeight = mathMax(0, pick(optionsChart.height, // the offsetHeight of an empty container is 0 in standard browsers, but 19 in IE7: - (chart.containerHeight > 19 ? chart.containerHeight : 400); + chart.containerHeight > 19 ? chart.containerHeight : 400)); }, /** @@ -9998,6 +11083,8 @@ chartWidth, chartHeight, renderTo, + indexAttrName = 'data-highcharts-chart', + oldChartIndex, containerId; chart.renderTo = renderTo = optionsChart.renderTo; @@ -10011,6 +11098,15 @@ if (!renderTo) { error(13, true); } + + // If the container already holds a chart, destroy it + oldChartIndex = pInt(attr(renderTo, indexAttrName)); + if (!isNaN(oldChartIndex) && charts[oldChartIndex]) { + charts[oldChartIndex].destroy(); + } + + // Make a reference to the chart from the div + attr(renderTo, indexAttrName, chart.index); // remove previous chart renderTo.innerHTML = ''; @@ -10040,11 +11136,16 @@ width: chartWidth + PX, height: chartHeight + PX, textAlign: 'left', - lineHeight: 'normal' // #427 + lineHeight: 'normal', // #427 + zIndex: 0, // #1072 + '-webkit-tap-highlight-color': 'rgba(0,0,0,0)' }, optionsChart.style), chart.renderToClone || renderTo ); + // cache the cursor (#1650) + chart._cursor = container.style.cursor; + chart.renderer = optionsChart.forExport ? // force SVG, used for SVG export new SVGRenderer(container, chartWidth, chartHeight, true) : @@ -10075,8 +11176,8 @@ optionsMarginLeft = chart.optionsMarginLeft, optionsMarginRight = chart.optionsMarginRight, optionsMarginBottom = chart.optionsMarginBottom, - chartTitleOptions = chart.chartTitleOptions, - chartSubtitleOptions = chart.chartSubtitleOptions, + chartTitleOptions = chart.options.title, + chartSubtitleOptions = chart.options.subtitle, legendOptions = chart.options.legend, legendMargin = pick(legendOptions.margin, 10), legendX = legendOptions.x, @@ -10172,9 +11273,9 @@ initReflow: function () { var chart = this, optionsChart = chart.options.chart, - renderTo = chart.renderTo; - - var reflowTimeout; + renderTo = chart.renderTo, + reflowTimeout; + function reflow(e) { var width = optionsChart.width || adapterRun(renderTo, 'width'), height = optionsChart.height || adapterRun(renderTo, 'height'), @@ -10182,12 +11283,15 @@ // Width and height checks for display:none. Target is doc in IE8 and Opera, // win in Firefox, Chrome and IE9. - if (width && height && (target === win || target === doc)) { + if (!chart.hasUserSize && width && height && (target === win || target === doc)) { if (width !== chart.containerWidth || height !== chart.containerHeight) { clearTimeout(reflowTimeout); - reflowTimeout = setTimeout(function () { - chart.resize(width, height, false); + chart.reflowTimeout = reflowTimeout = setTimeout(function () { + if (chart.container) { // It may have been destroyed in the meantime (#1257) + chart.setSize(width, height, false); + chart.hasUserSize = null; + } }, 100); } chart.containerWidth = width; @@ -10201,34 +11305,26 @@ }, /** - * Fires endResize event on chart instance. - */ - fireEndResize: function () { - var chart = this; - - if (chart) { - fireEvent(chart, 'endResize', null, function () { - chart.isResizing -= 1; - }); - } - }, - - /** * Resize the chart to a given width and height * @param {Number} width * @param {Number} height * @param {Object|Boolean} animation */ - // TODO: This method is called setSize in the api - resize: function (width, height, animation) { + setSize: function (width, height, animation) { var chart = this, chartWidth, chartHeight, - spacingBox, - chartTitle = chart.title, - chartSubtitle = chart.subtitle; + fireEndResize; + // Handle the isResizing counter chart.isResizing += 1; + fireEndResize = function () { + if (chart) { + fireEvent(chart, 'endResize', null, function () { + chart.isResizing -= 1; + }); + } + }; // set the animation for the current process setAnimation(animation, chart); @@ -10236,22 +11332,20 @@ chart.oldChartHeight = chart.chartHeight; chart.oldChartWidth = chart.chartWidth; if (defined(width)) { - chart.chartWidth = chartWidth = mathRound(width); + chart.chartWidth = chartWidth = mathMax(0, mathRound(width)); + chart.hasUserSize = !!chartWidth; } if (defined(height)) { - chart.chartHeight = chartHeight = mathRound(height); + chart.chartHeight = chartHeight = mathMax(0, mathRound(height)); } css(chart.container, { width: chartWidth + PX, height: chartHeight + PX }); + chart.setChartSize(true); chart.renderer.setSize(chartWidth, chartHeight, animation); - // update axis lengths for more correct tick intervals: - chart.plotWidth = chartWidth - chart.plotLeft - chart.marginRight; - chart.plotHeight = chartHeight - chart.plotTop - chart.marginBottom; - // handle axes chart.maxTicks = null; each(chart.axes, function (axis) { @@ -10269,15 +11363,6 @@ chart.getMargins(); - // move titles - spacingBox = chart.spacingBox; - if (chartTitle) { - chartTitle.align(null, null, spacingBox); - } - if (chartSubtitle) { - chartSubtitle.align(null, null, spacingBox); - } - chart.redraw(animation); @@ -10287,9 +11372,9 @@ // fire endResize and set isResizing back // If animation is disabled, fire without delay if (globalAnimation === false) { - chart.fireEndResize(); + fireEndResize(); } else { // else set a timeout with the animation duration - setTimeout(chart.fireEndResize, (globalAnimation && globalAnimation.duration) || 500); + setTimeout(fireEndResize, (globalAnimation && globalAnimation.duration) || 500); } }, @@ -10297,36 +11382,64 @@ * Set the public chart properties. This is done before and after the pre-render * to determine margin sizes */ - setChartSize: function () { + setChartSize: function (skipAxes) { var chart = this, inverted = chart.inverted, + renderer = chart.renderer, chartWidth = chart.chartWidth, chartHeight = chart.chartHeight, optionsChart = chart.options.chart, spacingTop = optionsChart.spacingTop, spacingRight = optionsChart.spacingRight, spacingBottom = optionsChart.spacingBottom, - spacingLeft = optionsChart.spacingLeft; - - chart.plotLeft = mathRound(chart.plotLeft); - chart.plotTop = mathRound(chart.plotTop); - chart.plotWidth = mathRound(chartWidth - chart.plotLeft - chart.marginRight); - chart.plotHeight = mathRound(chartHeight - chart.plotTop - chart.marginBottom); + spacingLeft = optionsChart.spacingLeft, + clipOffset = chart.clipOffset, + clipX, + clipY, + plotLeft, + plotTop, + plotWidth, + plotHeight, + plotBorderWidth; + + chart.plotLeft = plotLeft = mathRound(chart.plotLeft); + chart.plotTop = plotTop = mathRound(chart.plotTop); + chart.plotWidth = plotWidth = mathMax(0, mathRound(chartWidth - plotLeft - chart.marginRight)); + chart.plotHeight = plotHeight = mathMax(0, mathRound(chartHeight - plotTop - chart.marginBottom)); - chart.plotSizeX = inverted ? chart.plotHeight : chart.plotWidth; - chart.plotSizeY = inverted ? chart.plotWidth : chart.plotHeight; + chart.plotSizeX = inverted ? plotHeight : plotWidth; + chart.plotSizeY = inverted ? plotWidth : plotHeight; + + chart.plotBorderWidth = plotBorderWidth = optionsChart.plotBorderWidth || 0; - chart.spacingBox = { + // Set boxes used for alignment + chart.spacingBox = renderer.spacingBox = { x: spacingLeft, y: spacingTop, width: chartWidth - spacingLeft - spacingRight, height: chartHeight - spacingTop - spacingBottom }; + chart.plotBox = renderer.plotBox = { + x: plotLeft, + y: plotTop, + width: plotWidth, + height: plotHeight + }; + clipX = mathCeil(mathMax(plotBorderWidth, clipOffset[3]) / 2); + clipY = mathCeil(mathMax(plotBorderWidth, clipOffset[0]) / 2); + chart.clipBox = { + x: clipX, + y: clipY, + width: mathFloor(chart.plotSizeX - mathMax(plotBorderWidth, clipOffset[1]) / 2 - clipX), + height: mathFloor(chart.plotSizeY - mathMax(plotBorderWidth, clipOffset[2]) / 2 - clipY) + }; - each(chart.axes, function (axis) { - axis.setAxisSize(); - axis.setAxisTranslation(); - }); + if (!skipAxes) { + each(chart.axes, function (axis) { + axis.setAxisSize(); + axis.setAxisTranslation(); + }); + } }, /** @@ -10345,6 +11458,7 @@ chart.marginBottom = pick(chart.optionsMarginBottom, spacingBottom); chart.plotLeft = pick(chart.optionsMarginLeft, spacingLeft); chart.axisOffset = [0, 0, 0, 0]; // top, right, bottom, left + chart.clipOffset = [0, 0, 0, 0]; }, /** @@ -10364,14 +11478,16 @@ chartBackgroundColor = optionsChart.backgroundColor, plotBackgroundColor = optionsChart.plotBackgroundColor, plotBackgroundImage = optionsChart.plotBackgroundImage, + plotBorderWidth = optionsChart.plotBorderWidth || 0, mgn, bgAttr, - plotSize = { - x: chart.plotLeft, - y: chart.plotTop, - width: chart.plotWidth, - height: chart.plotHeight - }; + plotLeft = chart.plotLeft, + plotTop = chart.plotTop, + plotWidth = chart.plotWidth, + plotHeight = chart.plotHeight, + plotBox = chart.plotBox, + clipRect = chart.clipRect, + clipBox = chart.clipBox; // Chart area mgn = chartBorderWidth + (optionsChart.shadow ? 8 : 0); @@ -10391,6 +11507,7 @@ .attr(bgAttr) .add() .shadow(optionsChart.shadow); + } else { // resize chartBackground.animate( chartBackground.crisp(null, null, null, chartWidth - mgn, chartHeight - mgn) @@ -10402,38 +11519,48 @@ // Plot background if (plotBackgroundColor) { if (!plotBackground) { - chart.plotBackground = renderer.rect(chart.plotLeft, chart.plotTop, chart.plotWidth, chart.plotHeight, 0) + chart.plotBackground = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0) .attr({ fill: plotBackgroundColor }) .add() .shadow(optionsChart.plotShadow); } else { - plotBackground.animate(plotSize); + plotBackground.animate(plotBox); } } if (plotBackgroundImage) { if (!plotBGImage) { - chart.plotBGImage = renderer.image(plotBackgroundImage, chart.plotLeft, chart.plotTop, chart.plotWidth, chart.plotHeight) + chart.plotBGImage = renderer.image(plotBackgroundImage, plotLeft, plotTop, plotWidth, plotHeight) .add(); } else { - plotBGImage.animate(plotSize); + plotBGImage.animate(plotBox); } } + + // Plot clip + if (!clipRect) { + chart.clipRect = renderer.clipRect(clipBox); + } else { + clipRect.animate({ + width: clipBox.width, + height: clipBox.height + }); + } // Plot area border - if (optionsChart.plotBorderWidth) { + if (plotBorderWidth) { if (!plotBorder) { - chart.plotBorder = renderer.rect(chart.plotLeft, chart.plotTop, chart.plotWidth, chart.plotHeight, 0, optionsChart.plotBorderWidth) + chart.plotBorder = renderer.rect(plotLeft, plotTop, plotWidth, plotHeight, 0, plotBorderWidth) .attr({ stroke: optionsChart.plotBorderColor, - 'stroke-width': optionsChart.plotBorderWidth, - zIndex: 4 + 'stroke-width': plotBorderWidth, + zIndex: 1 }) .add(); } else { plotBorder.animate( - plotBorder.crisp(null, chart.plotLeft, chart.plotTop, chart.plotWidth, chart.plotHeight) + plotBorder.crisp(null, plotLeft, plotTop, plotWidth, plotHeight) ); } } @@ -10501,7 +11628,7 @@ // Legend - chart.legend = new Legend(chart); + chart.legend = new Legend(chart, options.legend); // Get margins by pre-rendering axes // set axes scales @@ -10544,8 +11671,8 @@ // Labels if (labels.items) { - each(labels.items, function () { - var style = extend(labels.style, this.style), + each(labels.items, function (label) { + var style = extend(labels.style, label.style), x = pInt(style.left) + chart.plotLeft, y = pInt(style.top) + chart.plotTop + 12; @@ -10554,7 +11681,7 @@ delete style.top; renderer.text( - this.html, + label.html, x, y ) @@ -10599,20 +11726,16 @@ var chart = this, axes = chart.axes, series = chart.series, - container = chart.container; - - var i, + container = chart.container, + i, parentNode = container && container.parentNode; - - // If the chart is destroyed already, do nothing. - // This will happen if if a script invokes chart.destroy and - // then it will be called again on win.unload - if (chart === null) { - return; - } - + // fire the chart.destoy event fireEvent(chart, 'destroy'); + + // Delete the chart from charts lookup array + charts[chart.index] = UNDEFINED; + chart.renderTo.removeAttribute('data-highcharts-chart'); // remove events removeEvent(chart); @@ -10631,10 +11754,12 @@ } // ==== Destroy chart properties: - each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage', 'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'tracker', 'scroller', 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) { + each(['title', 'subtitle', 'chartBackground', 'plotBackground', 'plotBGImage', + 'plotBorder', 'seriesGroup', 'clipRect', 'credits', 'pointer', 'scroller', + 'rangeSelector', 'legend', 'resetZoomButton', 'tooltip', 'renderer'], function (name) { var prop = chart[name]; - if (prop) { + if (prop && prop.destroy) { chart[name] = prop.destroy(); } }); @@ -10647,8 +11772,6 @@ discardElement(container); } - // IE6 leak - container = null; } // clean it all up @@ -10656,51 +11779,56 @@ delete chart[i]; } - chart.options = null; - chart = null; }, + /** - * Prepare for first rendering after all data are loaded + * VML namespaces can't be added until after complete. Listening + * for Perini's doScroll hack is not enough. */ - firstRender: function () { - var chart = this, - options = chart.options, - callback = chart.callback; + isReadyToRender: function () { + var chart = this; - // VML namespaces can't be added until after complete. Listening - // for Perini's doScroll hack is not enough. - var ONREADYSTATECHANGE = 'onreadystatechange', - COMPLETE = 'complete'; // Note: in spite of JSLint's complaints, win == win.top is required /*jslint eqeq: true*/ - if ((!hasSVG && (win == win.top && doc.readyState !== COMPLETE)) || (useCanVG && !win.canvg)) { + if ((!hasSVG && (win == win.top && doc.readyState !== 'complete')) || (useCanVG && !win.canvg)) { /*jslint eqeq: false*/ if (useCanVG) { // Delay rendering until canvg library is downloaded and ready - CanVGController.push(function () { chart.firstRender(); }, options.global.canvasToolsURL); + CanVGController.push(function () { chart.firstRender(); }, chart.options.global.canvasToolsURL); } else { - doc.attachEvent(ONREADYSTATECHANGE, function () { - doc.detachEvent(ONREADYSTATECHANGE, chart.firstRender); - if (doc.readyState === COMPLETE) { + doc.attachEvent('onreadystatechange', function () { + doc.detachEvent('onreadystatechange', chart.firstRender); + if (doc.readyState === 'complete') { chart.firstRender(); } }); } + return false; + } + return true; + }, + + /** + * Prepare for first rendering after all data are loaded + */ + firstRender: function () { + var chart = this, + options = chart.options, + callback = chart.callback; + + // Check whether the chart is ready to render + if (!chart.isReadyToRender()) { return; } - // create the container + // Create the container chart.getContainer(); // Run an early event after the container and renderer are established fireEvent(chart, 'init'); - // Initialize range selector for stock charts - if (Highcharts.RangeSelector && options.rangeSelector.enabled) { - chart.rangeSelector = new Highcharts.RangeSelector(chart); - } - + chart.resetMargins(); chart.setChartSize(); @@ -10715,16 +11843,13 @@ chart.initSeries(serieOptions); }); - // Run an event where series and axes can be added - //fireEvent(chart, 'beforeRender'); - - // Initialize scroller for stock charts - if (Highcharts.Scroller && (options.navigator.enabled || options.scrollbar.enabled)) { - chart.scroller = new Highcharts.Scroller(chart); - } + // Run an event after axes and series are initialized, but before render. At this stage, + // the series data is indexed and cached in the xData and yData arrays, so we can access + // those before rendering. Used in Highstock. + fireEvent(chart, 'beforeRender'); // depends on inverted and on margins being set - chart.tracker = new MouseTracker(chart, options); + chart.pointer = new Pointer(chart, options); chart.render(); @@ -10744,73 +11869,6 @@ fireEvent(chart, 'load'); - }, - - init: function (chartEvents) { - var chart = this, - optionsChart = chart.options.chart, - eventType; - - // Run chart - - // Set up auto resize - if (optionsChart.reflow !== false) { - addEvent(chart, 'load', chart.initReflow); - } - - // Chart event handlers - if (chartEvents) { - for (eventType in chartEvents) { - addEvent(chart, eventType, chartEvents[eventType]); - } - } - - chart.xAxis = []; - chart.yAxis = []; - - // Expose methods and variables - chart.animation = useCanVG ? false : pick(optionsChart.animation, true); - chart.setSize = chart.resize; - chart.pointCount = 0; - chart.counters = new ChartCounters(); - /* - if ($) $(function () { - $container = $('#container'); - var origChartWidth, - origChartHeight; - if ($container) { - $('') - .insertBefore($container) - .click(function () { - if (origChartWidth === UNDEFINED) { - origChartWidth = chartWidth; - origChartHeight = chartHeight; - } - chart.resize(chartWidth *= 1.1, chartHeight *= 1.1); - }); - $('') - .insertBefore($container) - .click(function () { - if (origChartWidth === UNDEFINED) { - origChartWidth = chartWidth; - origChartHeight = chartHeight; - } - chart.resize(chartWidth *= 0.9, chartHeight *= 0.9); - }); - $('') - .insertBefore($container) - .click(function () { - if (origChartWidth === UNDEFINED) { - origChartWidth = chartWidth; - origChartHeight = chartHeight; - } - chart.resize(origChartWidth, origChartHeight); - }); - } - }) - */ - - chart.firstRender(); } }; // end Chart @@ -10828,22 +11886,20 @@ * @param {Object} options The data in either number, array or object format */ init: function (series, options, x) { + var point = this, - counters = series.chart.counters, - defaultColors; + colors; point.series = series; point.applyOptions(options, x); point.pointAttr = {}; if (series.options.colorByPoint) { - defaultColors = series.chart.options.colors; - if (!point.options) { - point.options = {}; - } - point.color = point.options.color = point.color || defaultColors[counters.color++]; - + colors = series.options.colors || series.chart.options.colors; + point.color = point.color || colors[series.colorCounter++]; // loop back to zero - counters.wrapColor(defaultColors.length); + if (series.colorCounter === colors.length) { + series.colorCounter = 0; + } } series.chart.pointCount++; @@ -10858,42 +11914,73 @@ applyOptions: function (options, x) { var point = this, series = point.series, - optionsType = typeof options; + pointValKey = series.pointValKey; - point.config = options; + options = Point.prototype.optionsToObject.call(this, options); - // onedimensional array input - if (optionsType === 'number' || options === null) { - point.y = options; - } else if (typeof options[0] === 'number') { // two-dimentional array - point.x = options[0]; - point.y = options[1]; - } else if (optionsType === 'object' && typeof options.length !== 'number') { // object input - // copy options directly to point - extend(point, options); - point.options = options; + // copy options directly to point + extend(point, options); + point.options = point.options ? extend(point.options, options) : options; - // This is the fastest way to detect if there are individual point dataLabels that need - // to be considered in drawDataLabels. These can only occur in object configs. - if (options.dataLabels) { - series._hasPointLabels = true; - } - } else if (typeof options[0] === 'string') { // categorized data with name in first position - point.name = options[0]; - point.y = options[1]; + // For higher dimension series types. For instance, for ranges, point.y is mapped to point.low. + if (pointValKey) { + point.y = point[pointValKey]; } - /* - * If no x is set by now, get auto incremented value. All points must have an - * x value, however the y value can be null to create a gap in the series - */ - // todo: skip this? It is only used in applyOptions, in translate it should not be used - if (point.x === UNDEFINED) { + // If no x is set by now, get auto incremented value. All points must have an + // x value, however the y value can be null to create a gap in the series + if (point.x === UNDEFINED && series) { point.x = x === UNDEFINED ? series.autoIncrement() : x; } - + return point; + }, + + /** + * Transform number or array configs into objects + */ + optionsToObject: function (options) { + var ret, + series = this.series, + pointArrayMap = series.pointArrayMap || ['y'], + valueCount = pointArrayMap.length, + firstItemType, + i = 0, + j = 0; + + if (typeof options === 'number' || options === null) { + ret = { y: options }; + + } else if (isArray(options)) { + ret = {}; + // with leading x value + if (options.length > valueCount) { + firstItemType = typeof options[0]; + if (firstItemType === 'string') { + ret.name = options[0]; + } else if (firstItemType === 'number') { + ret.x = options[0]; + } + i++; + } + while (j < valueCount) { + ret[pointArrayMap[j++]] = options[i++]; + } + } else if (typeof options === 'object') { + ret = options; + + // This is the fastest way to detect if there are individual point dataLabels that need + // to be considered in drawDataLabels. These can only occur in object configs. + if (options.dataLabels) { + series._hasPointLabels = true; + } + // Same approach as above for markers + if (options.marker) { + series._hasPointMarkers = true; + } + } + return ret; }, /** @@ -10942,7 +12029,7 @@ */ destroyElements: function () { var point = this, - props = ['graphic', 'tracker', 'dataLabel', 'group', 'connector', 'shadowGroup'], + props = ['graphic', 'dataLabel', 'dataLabelUpper', 'group', 'connector', 'shadowGroup'], prop, i = 6; while (i--) { @@ -10984,14 +12071,17 @@ // fire the event with the defalut handler point.firePointEvent(selected ? 'select' : 'unselect', { accumulate: accumulate }, function () { - point.selected = selected; + point.selected = point.options.selected = selected; + series.options.data[inArray(point, series.data)] = point.options; + point.setState(selected && SELECT_STATE); // unselect all other points unless Ctrl or Cmd + click if (!accumulate) { each(chart.getSelectedPoints(), function (loopPoint) { if (loopPoint.selected && loopPoint !== point) { - loopPoint.selected = false; + loopPoint.selected = loopPoint.options.selected = false; + series.options.data[inArray(loopPoint, series.data)] = loopPoint.options; loopPoint.setState(NORMAL_STATE); loopPoint.firePointEvent('unselect'); } @@ -11000,7 +12090,10 @@ }); }, - onMouseOver: function () { + /** + * Runs on mouse over the point + */ + onMouseOver: function (e) { var point = this, series = point.series, chart = series.chart, @@ -11017,20 +12110,27 @@ // update the tooltip if (tooltip && (!tooltip.shared || series.noSharedTooltip)) { - tooltip.refresh(point); + tooltip.refresh(point, e); } // hover this point.setState(HOVER_STATE); chart.hoverPoint = point; }, - + + /** + * Runs on mouse out from the point + */ onMouseOut: function () { - var point = this; - point.firePointEvent('mouseOut'); - - point.setState(); - point.series.chart.hoverPoint = null; + var chart = this.series.chart, + hoverPoints = chart.hoverPoints; + + if (!hoverPoints || inArray(this, hoverPoints) === -1) { // #887 + this.firePointEvent('mouseOut'); + + this.setState(); + chart.hoverPoint = null; + } }, /** @@ -11039,60 +12139,27 @@ * @return {String} A string to be concatenated in to the common tooltip text */ tooltipFormatter: function (pointFormat) { - var point = this, - series = point.series, - seriesTooltipOptions = series.tooltipOptions, - match = pointFormat.match(/\{(series|point)\.[a-zA-Z]+\}/g), - splitter = /[{\.}]/, - obj, - key, - replacement, - repOptionKey, - parts, - prop, - i, - cfg = { // docs: percentageDecimals, percentagePrefix, percentageSuffix, totalDecimals, totalPrefix, totalSuffix - y: 0, // 0: use 'value' for repOptionKey - open: 0, - high: 0, - low: 0, - close: 0, - percentage: 1, // 1: use the self name for repOptionKey - total: 1 - }; - - // Backwards compatibility to y naming in early Highstock - seriesTooltipOptions.valuePrefix = seriesTooltipOptions.valuePrefix || seriesTooltipOptions.yPrefix; - seriesTooltipOptions.valueDecimals = seriesTooltipOptions.valueDecimals || seriesTooltipOptions.yDecimals; - seriesTooltipOptions.valueSuffix = seriesTooltipOptions.valueSuffix || seriesTooltipOptions.ySuffix; - - // loop over the variables defined on the form {series.name}, {point.y} etc - for (i in match) { - key = match[i]; - if (isString(key) && key !== pointFormat) { // IE matches more than just the variables - - // Split it further into parts - parts = (' ' + key).split(splitter); // add empty string because IE and the rest handles it differently - obj = { 'point': point, 'series': series }[parts[1]]; - prop = parts[2]; - - // Add some preformatting - if (obj === point && cfg.hasOwnProperty(prop)) { - repOptionKey = cfg[prop] ? prop : 'value'; - replacement = (seriesTooltipOptions[repOptionKey + 'Prefix'] || '') + - numberFormat(point[prop], pick(seriesTooltipOptions[repOptionKey + 'Decimals'], -1)) + - (seriesTooltipOptions[repOptionKey + 'Suffix'] || ''); - - // Automatic replacement - } else { - replacement = obj[prop]; - } - - pointFormat = pointFormat.replace(key, replacement); - } - } - return pointFormat; + // Insert options for valueDecimals, valuePrefix, and valueSuffix + var series = this.series, + seriesTooltipOptions = series.tooltipOptions, + valueDecimals = pick(seriesTooltipOptions.valueDecimals, ''), + valuePrefix = seriesTooltipOptions.valuePrefix || '', + valueSuffix = seriesTooltipOptions.valueSuffix || ''; + + // Loop over the point array map and replace unformatted values with sprintf formatting markup + each(series.pointArrayMap || ['y'], function (key) { + key = '{point.' + key; // without the closing bracket + if (valuePrefix || valueSuffix) { + pointFormat = pointFormat.replace(key + '}', valuePrefix + key + '}' + valueSuffix); + } + pointFormat = pointFormat.replace(key + '}', key + ':,.' + valueDecimals + 'f}'); + }); + + return format(pointFormat, { + point: this, + series: this.series + }); }, /** @@ -11110,7 +12177,6 @@ graphic = point.graphic, i, data = series.data, - dataLength = data.length, chart = series.chart; redraw = pick(redraw, true); @@ -11129,14 +12195,11 @@ } // record changes in the parallel arrays - for (i = 0; i < dataLength; i++) { - if (data[i] === point) { - series.xData[i] = point.x; - series.yData[i] = point.y; - series.options.data[i] = options; - break; - } - } + i = inArray(point, data); + series.xData[i] = point.x; + series.yData[i] = series.toYData ? series.toYData(point) : point.y; + series.zData[i] = point.z; + series.options.data[i] = point.options; // redraw series.isDirty = true; @@ -11158,8 +12221,7 @@ series = point.series, chart = series.chart, i, - data = series.data, - dataLength = data.length; + data = series.data; setAnimation(animation, chart); redraw = pick(redraw, true); @@ -11167,19 +12229,13 @@ // fire the event with a default handler of removing the point point.firePointEvent('remove', null, function () { - //erase(series.data, point); - - for (i = 0; i < dataLength; i++) { - if (data[i] === point) { - - // splice all the parallel arrays - data.splice(i, 1); - series.options.data.splice(i, 1); - series.xData.splice(i, 1); - series.yData.splice(i, 1); - break; - } - } + // splice all the parallel arrays + i = inArray(point, data); + data.splice(i, 1); + series.options.data.splice(i, 1); + series.xData.splice(i, 1); + series.yData.splice(i, 1); + series.zData.splice(i, 1); point.destroy(); @@ -11258,8 +12314,10 @@ markerStateOptions = markerOptions && markerOptions.states[state], stateDisabled = markerStateOptions && markerStateOptions.enabled === false, stateMarkerGraphic = series.stateMarkerGraphic, + pointMarker = point.marker || {}, chart = series.chart, radius, + newSymbol, pointAttr = point.pointAttr; state = state || NORMAL_STATE; // empty string @@ -11294,27 +12352,39 @@ // if a graphic is not applied to each point in the normal state, create a shared // graphic for the hover state if (state && markerStateOptions) { + radius = markerStateOptions.radius; + newSymbol = pointMarker.symbol || series.symbol; + + // If the point has another symbol than the previous one, throw away the + // state marker graphic and force a new one (#1459) + if (stateMarkerGraphic && stateMarkerGraphic.currentSymbol !== newSymbol) { + stateMarkerGraphic = stateMarkerGraphic.destroy(); + } + + // Add a new state marker graphic if (!stateMarkerGraphic) { - radius = markerStateOptions.radius; series.stateMarkerGraphic = stateMarkerGraphic = chart.renderer.symbol( - series.symbol, - -radius, - -radius, + newSymbol, + plotX - radius, + plotY - radius, 2 * radius, 2 * radius ) .attr(pointAttr[state]) - .add(series.group); + .add(series.markerGroup); + stateMarkerGraphic.currentSymbol = newSymbol; + + // Move the existing graphic + } else { + stateMarkerGraphic.attr({ // #1054 + x: plotX - radius, + y: plotY - radius + }); } - - stateMarkerGraphic.translate( - plotX, - plotY - ); } if (stateMarkerGraphic) { - stateMarkerGraphic[state ? 'show' : 'hide'](); + stateMarkerGraphic[state && chart.isInsidePlot(plotX, plotY) ? 'show' : 'hide'](); } } @@ -11349,29 +12419,30 @@ type: 'line', pointClass: Point, sorted: true, // requires the data to be sorted + requireSorting: true, pointAttrToOptions: { // mapping between SVG attributes and the corresponding options stroke: 'lineColor', 'stroke-width': 'lineWidth', fill: 'fillColor', r: 'radius' }, + colorCounter: 0, init: function (chart, options) { var series = this, eventType, events, - //pointEvent, - index = chart.series.length; + linkedTo, + chartSeries = chart.series; series.chart = chart; series.options = options = series.setOptions(options); // merge with plotOptions - + // bind the axes series.bindAxes(); // set some variables extend(series, { - index: index, - name: options.name || 'Series ' + (index + 1), + name: options.name, state: NORMAL_STATE, pointAttr: {}, visible: options.visible !== false, // true by default @@ -11408,7 +12479,32 @@ } // Register it in the chart - chart.series.push(series); + chartSeries.push(series); + series._i = chartSeries.length - 1; + + // Sort series according to index option (#248, #1123) + stableSort(chartSeries, function (a, b) { + return pick(a.options.index, a._i) - pick(b.options.index, a._i); + }); + each(chartSeries, function (series, i) { + series.index = i; + series.name = series.name || 'Series ' + (i + 1); + }); + + // Linked series + linkedTo = options.linkedTo; + series.linkedSeries = []; + if (isString(linkedTo)) { + if (linkedTo === ':previous') { + linkedTo = chartSeries[series.index - 1]; + } else { + linkedTo = chart.get(linkedTo); + } + if (linkedTo) { + linkedTo.linkedSeries.push(series); + series.linkedParent = linkedTo; + } + } }, /** @@ -11432,6 +12528,7 @@ // apply if the series xAxis or yAxis option mathches the number of the // axis, or if undefined, use the first axis if ((seriesOptions[AXIS] === axisOptions.index) || + (seriesOptions[AXIS] !== UNDEFINED && seriesOptions[AXIS] === axisOptions.id) || (seriesOptions[AXIS] === UNDEFINED && axisOptions.index === 0)) { // register this series in the axis.series lookup @@ -11444,7 +12541,12 @@ axis.isDirty = true; } }); - + + // The series needs an X and an Y axis + if (!series[AXIS]) { + error(18, true); + } + }); } }, @@ -11515,26 +12617,27 @@ * @param {Object} itemOptions */ setOptions: function (itemOptions) { - var series = this, - chart = series.chart, + var chart = this.chart, chartOptions = chart.options, plotOptions = chartOptions.plotOptions, - data = itemOptions.data, + typeOptions = plotOptions[this.type], options; - itemOptions.data = null; // remove from merge to prevent looping over the data set + this.userOptions = itemOptions; options = merge( - plotOptions[this.type], + typeOptions, plotOptions.series, itemOptions ); - // Re-insert the data array to the options and the original config (#717) - options.data = itemOptions.data = data; - // the tooltip options are merged between global and series specific options - series.tooltipOptions = merge(chartOptions.tooltip, options.tooltip); + this.tooltipOptions = merge(chartOptions.tooltip, options.tooltip); + + // Delte marker object if not allowed (#1125) + if (typeOptions.marker === null) { + delete options.marker; + } return options; @@ -11544,10 +12647,25 @@ */ getColor: function () { var options = this.options, + userOptions = this.userOptions, defaultColors = this.chart.options.colors, - counters = this.chart.counters; - this.color = options.color || - (!options.colorByPoint && defaultColors[counters.color++]) || 'gray'; + counters = this.chart.counters, + color, + colorIndex; + + color = options.color || defaultPlotOptions[this.type].color; + + if (!color && !options.colorByPoint) { + if (defined(userOptions._colorIndex)) { // after Series.update() + colorIndex = userOptions._colorIndex; + } else { + userOptions._colorIndex = counters.color; + colorIndex = counters.color++; + } + color = defaultColors[colorIndex]; + } + + this.color = color; counters.wrapColor(defaultColors.length); }, /** @@ -11555,12 +12673,24 @@ */ getSymbol: function () { var series = this, + userOptions = series.userOptions, seriesMarkerOption = series.options.marker, chart = series.chart, defaultSymbols = chart.options.symbols, - counters = chart.counters; - series.symbol = seriesMarkerOption.symbol || defaultSymbols[counters.symbol++]; - + counters = chart.counters, + symbolIndex; + + series.symbol = seriesMarkerOption.symbol; + if (!series.symbol) { + if (defined(userOptions._symbolIndex)) { // after Series.update() + symbolIndex = userOptions._symbolIndex; + } else { + userOptions._symbolIndex = counters.symbol; + symbolIndex = counters.symbol++; + } + series.symbol = defaultSymbols[symbolIndex]; + } + // don't substract radius in image symbols (#604) if (/^url/.test(series.symbol)) { seriesMarkerOption.radius = 0; @@ -11617,7 +12747,6 @@ 2 * radius, 2 * radius ) - .attr(this.pointAttr[NORMAL_STATE]) .add(legendItemGroup); } }, @@ -11633,16 +12762,18 @@ */ addPoint: function (options, redraw, shift, animation) { var series = this, + seriesOptions = series.options, data = series.data, graph = series.graph, area = series.area, chart = series.chart, xData = series.xData, yData = series.yData, + zData = series.zData, + names = series.names, currentShift = (graph && graph.shift) || 0, - dataOptions = series.options.data, + dataOptions = seriesOptions.data, point; - //point = (new series.pointClass()).init(series, options); setAnimation(animation, chart); @@ -11665,9 +12796,17 @@ point = { series: series }; series.pointClass.prototype.applyOptions.apply(point, [options]); xData.push(point.x); - yData.push(series.valueCount === 4 ? [point.open, point.high, point.low, point.close] : point.y); + yData.push(series.toYData ? series.toYData(point) : point.y); + zData.push(point.z); + if (names) { + names[point.x] = point.name; + } dataOptions.push(options); + // Generate points to be added to the legend (#1329) + if (seriesOptions.legendType === 'point') { + series.generatePoints(); + } // Shift the first point off the parallel arrays // todo: consider series.removePoint(i) method @@ -11678,6 +12817,7 @@ data.shift(); xData.shift(); yData.shift(); + zData.shift(); dataOptions.shift(); } } @@ -11700,28 +12840,28 @@ var series = this, oldData = series.points, options = series.options, - initialColor = series.initialColor, chart = series.chart, firstPoint = null, xAxis = series.xAxis, - i, - pointProto = series.pointClass.prototype; + names = xAxis && xAxis.categories && !xAxis.categories.length ? [] : null, + i; // reset properties series.xIncrement = null; - series.pointRange = (xAxis && xAxis.categories && 1) || options.pointRange; + series.pointRange = xAxis && xAxis.categories ? 1 : options.pointRange; - if (defined(initialColor)) { // reset colors for pie - chart.counters.color = initialColor; - } + series.colorCounter = 0; // for series with colorByPoint (#1547) // parallel arrays var xData = [], yData = [], + zData = [], dataLength = data ? data.length : [], turboThreshold = options.turboThreshold || 1000, pt, - valueCount = series.valueCount; + pointArrayMap = series.pointArrayMap, + valueCount = pointArrayMap && pointArrayMap.length, + hasToYData = !!series.toYData; // In turbo mode, only one- or twodimensional arrays of numbers are allowed. The // first value is tested, and we assume that all the rest are defined the same @@ -11766,17 +12906,36 @@ }*/ } else { for (i = 0; i < dataLength; i++) { - pt = { series: series }; - pointProto.applyOptions.apply(pt, [data[i]]); - xData[i] = pt.x; - yData[i] = pointProto.toYData ? pointProto.toYData.apply(pt) : pt.y; + if (data[i] !== UNDEFINED) { // stray commas in oldIE + pt = { series: series }; + series.pointClass.prototype.applyOptions.apply(pt, [data[i]]); + xData[i] = pt.x; + yData[i] = hasToYData ? series.toYData(pt) : pt.y; + zData[i] = pt.z; + if (names && pt.name) { + names[i] = pt.name; + } + } } } + + // Unsorted data is not supported by the line tooltip as well as data grouping and + // navigation in Stock charts (#725) + if (series.requireSorting && xData.length > 1 && xData[1] < xData[0]) { + error(15); + } + + // Forgetting to cast strings to numbers is a common caveat when handling CSV or JSON + if (isString(yData[0])) { + error(14, true); + } series.data = []; series.options.data = data; series.xData = xData; series.yData = yData; + series.zData = zData; + series.names = names; // destroy old points i = (oldData && oldData.length) || 0; @@ -11987,7 +13146,6 @@ } this.generatePoints(); var series = this, - chart = series.chart, options = series.options, stacking = options.stacking, xAxis = series.xAxis, @@ -11996,15 +13154,23 @@ points = series.points, dataLength = points.length, hasModifyValue = !!series.modifyValue, - isLastSeries, - allStackSeries = yAxis.series, - i = allStackSeries.length; - - // Is it the last visible series? + isBottomSeries, + allStackSeries, + i, + placeBetween = options.pointPlacement === 'between', + threshold = options.threshold; + //nextSeriesDown; + + // Is it the last visible series? (#809, #1722). + // TODO: After merging in the 'stacking' branch, this logic should probably be moved to Chart.getStacks + allStackSeries = yAxis.series.sort(function (a, b) { + return a.index - b.index; + }); + i = allStackSeries.length; while (i--) { if (allStackSeries[i].visible) { - if (i === series.index) { - isLastSeries = true; + if (allStackSeries[i] === series) { // #809 + isBottomSeries = true; } break; } @@ -12016,23 +13182,31 @@ xValue = point.x, yValue = point.y, yBottom = point.low, - stack = yAxis.stacks[(yValue < options.threshold ? '-' : '') + series.stackKey], + stack = yAxis.stacks[(yValue < threshold ? '-' : '') + series.stackKey], pointStack, pointStackTotal; - - // get the plotX translation - //point.plotX = mathRound(xAxis.translate(xValue, 0, 0, 0, 1) * 10) / 10; // Math.round fixes #591 - point.plotX = xAxis.translate(xValue, 0, 0, 0, 1); // Math.round fixes #591 - // calculate the bottom y value for stacked series + // Discard disallowed y values for log axes + if (yAxis.isLog && yValue <= 0) { + point.y = yValue = null; + } + + // Get the plotX translation + point.plotX = xAxis.translate(xValue, 0, 0, 0, 1, placeBetween); // Math.round fixes #591 + + // Calculate the bottom y value for stacked series if (stacking && series.visible && stack && stack[xValue]) { pointStack = stack[xValue]; pointStackTotal = pointStack.total; pointStack.cum = yBottom = pointStack.cum - yValue; // start from top yValue = yBottom + yValue; - if (isLastSeries) { - yBottom = options.threshold; + if (isBottomSeries) { + yBottom = pick(threshold, yAxis.min); + } + + if (yAxis.isLog && yBottom <= 0) { // #1200, #1232 + yBottom = null; } if (stacking === 'percent') { @@ -12041,7 +13215,7 @@ } point.percentage = pointStackTotal ? point.y * 100 / pointStackTotal : 0; - point.stackTotal = pointStackTotal; + point.total = point.stackTotal = pointStackTotal; point.stackY = yValue; } @@ -12056,14 +13230,14 @@ } // Set the the plotY value, reset it for redraws - point.plotY = (typeof yValue === 'number') ? + point.plotY = (typeof yValue === 'number' && yValue !== Infinity) ? mathRound(yAxis.translate(yValue, 0, 1, 0, 1) * 10) / 10 : // Math.round fixes #591 UNDEFINED; - - // set client related positions for mouse tracking - point.clientX = chart.inverted ? - chart.plotHeight - point.plotX : - point.plotX; // for mouse tracking + + // Set client related positions for mouse tracking + point.clientX = placeBetween ? xAxis.translate(xValue, 0, 0, 0, 1) : point.plotX; // #1514 + + point.negative = point.y < (threshold || 0); // some API data point.category = categories && categories[point.x] !== UNDEFINED ? @@ -12080,13 +13254,12 @@ */ setTooltipPoints: function (renew) { var series = this, - chart = series.chart, points = [], pointsLength, - plotSize = chart.plotSizeX, low, high, xAxis = series.xAxis, + axisLength = xAxis ? (xAxis.tooltipLen || xAxis.len) : series.chart.plotSizeX, // tooltipLen and tooltipPosName used in polar point, i, tooltipPoints = []; // a lookup array for each pixel in the x dimension @@ -12106,8 +13279,7 @@ points = points.concat(segment); }); - // loop the concatenated points and apply each point to all the closest - // pixel positions + // Reverse the points in case the X axis is reversed if (xAxis && xAxis.reversed) { points = points.reverse(); } @@ -12116,10 +13288,12 @@ pointsLength = points.length; for (i = 0; i < pointsLength; i++) { point = points[i]; - low = points[i - 1] ? points[i - 1]._high + 1 : 0; - point._high = high = points[i + 1] ? - mathMax(0, mathFloor((point.plotX + (points[i + 1] ? points[i + 1].plotX : plotSize)) / 2)) : - plotSize; + // Set this range's low to the last range's high plus one + low = points[i - 1] ? high + 1 : 0; + // Now find the new high + high = points[i + 1] ? + mathMax(0, mathFloor((point.clientX + (points[i + 1] ? points[i + 1].clientX : axisLength)) / 2)) : + axisLength; while (low >= 0 && low <= high) { tooltipPoints[low++] = point; @@ -12131,28 +13305,40 @@ /** * Format the header of the tooltip */ - tooltipHeaderFormatter: function (key) { + tooltipHeaderFormatter: function (point) { var series = this, tooltipOptions = series.tooltipOptions, xDateFormat = tooltipOptions.xDateFormat, + dateTimeLabelFormats = tooltipOptions.dateTimeLabelFormats, xAxis = series.xAxis, isDateTime = xAxis && xAxis.options.type === 'datetime', + headerFormat = tooltipOptions.headerFormat, + closestPointRange = xAxis && xAxis.closestPointRange, n; // Guess the best date format based on the closest point distance (#568) if (isDateTime && !xDateFormat) { - for (n in timeUnits) { - if (timeUnits[n] >= xAxis.closestPointRange) { - xDateFormat = tooltipOptions.dateTimeLabelFormats[n]; - break; - } - } + if (closestPointRange) { + for (n in timeUnits) { + if (timeUnits[n] >= closestPointRange) { + xDateFormat = dateTimeLabelFormats[n]; + break; + } + } + } else { + xDateFormat = dateTimeLabelFormats.day; + } + } + + // Insert the header date format if any + if (isDateTime && xDateFormat && isNumber(point.key)) { + headerFormat = headerFormat.replace('{point.key}', '{point.key:' + xDateFormat + '}'); } - return tooltipOptions.headerFormat - .replace('{point.key}', isDateTime ? dateFormat(xDateFormat, key) : key) - .replace('{series.name}', series.name) - .replace('{series.color}', series.color); + return format(headerFormat, { + point: point, + series: series + }); }, /** @@ -12163,10 +13349,6 @@ chart = series.chart, hoverSeries = chart.hoverSeries; - if (!hasTouch && chart.mouseIsDown) { - return; - } - // set normal state to previous series if (hoverSeries && hoverSeries !== series) { hoverSeries.onMouseOut(); @@ -12206,7 +13388,7 @@ // hide the tooltip - if (tooltip && !options.stickyTracking && !tooltip.shared) { + if (tooltip && !options.stickyTracking && (!tooltip.shared || series.noSharedTooltip)) { tooltip.hide(); } @@ -12221,29 +13403,86 @@ animate: function (init) { var series = this, chart = series.chart, - clipRect = series.clipRect, - animation = series.options.animation; + renderer = chart.renderer, + clipRect, + markerClipRect, + animation = series.options.animation, + clipBox = chart.clipBox, + inverted = chart.inverted, + sharedClipKey; + // Animation option is set to true if (animation && !isObject(animation)) { - animation = {}; + animation = defaultPlotOptions[series.type].animation; } + sharedClipKey = '_sharedClip' + animation.duration + animation.easing; - if (init) { // initialize the animation - if (!clipRect.isAnimating) { // apply it only for one of the series - clipRect.attr('width', 0); - clipRect.isAnimating = true; + // Initialize the animation. Set up the clipping rectangle. + if (init) { + + // If a clipping rectangle with the same properties is currently present in the chart, use that. + clipRect = chart[sharedClipKey]; + markerClipRect = chart[sharedClipKey + 'm']; + if (!clipRect) { + chart[sharedClipKey] = clipRect = renderer.clipRect( + extend(clipBox, { width: 0 }) + ); + + chart[sharedClipKey + 'm'] = markerClipRect = renderer.clipRect( + -99, // include the width of the first marker + inverted ? -chart.plotLeft : -chart.plotTop, + 99, + inverted ? chart.chartWidth : chart.chartHeight + ); + } + series.group.clip(clipRect); + series.markerGroup.clip(markerClipRect); + series.sharedClipKey = sharedClipKey; + + // Run the animation + } else { + clipRect = chart[sharedClipKey]; + if (clipRect) { + clipRect.animate({ + width: chart.plotSizeX + }, animation); + chart[sharedClipKey + 'm'].animate({ + width: chart.plotSizeX + 99 + }, animation); } - } else { // run the animation - clipRect.animate({ - width: chart.plotSizeX - }, animation); - - // delete this function to allow it only once - this.animate = null; + // Delete this function to allow it only once + series.animate = null; + + // Call the afterAnimate function on animation complete (but don't overwrite the animation.complete option + // which should be available to the user). + series.animationTimeout = setTimeout(function () { + series.afterAnimate(); + }, animation.duration); } }, - + + /** + * This runs after animation to land on the final plot clipping + */ + afterAnimate: function () { + var chart = this.chart, + sharedClipKey = this.sharedClipKey, + group = this.group; + + if (group && this.options.clip !== false) { + group.clip(chart.clipRect); + this.markerGroup.clip(); // no clip + } + + // Remove the shared clipping rectancgle when all series are shown + setTimeout(function () { + if (sharedClipKey && chart[sharedClipKey]) { + chart[sharedClipKey] = chart[sharedClipKey].destroy(); + chart[sharedClipKey + 'm'] = chart[sharedClipKey + 'm'].destroy(); + } + }, 100); + }, /** * Draw the markers @@ -12260,35 +13499,49 @@ radius, symbol, isImage, - graphic; + graphic, + options = series.options, + seriesMarkerOptions = options.marker, + pointMarkerOptions, + enabled, + isInside, + markerGroup = series.markerGroup; - if (series.options.marker.enabled) { + if (seriesMarkerOptions.enabled || series._hasPointMarkers) { + i = points.length; while (i--) { point = points[i]; plotX = point.plotX; plotY = point.plotY; graphic = point.graphic; - + pointMarkerOptions = point.marker || {}; + enabled = (seriesMarkerOptions.enabled && pointMarkerOptions.enabled === UNDEFINED) || pointMarkerOptions.enabled; + isInside = chart.isInsidePlot(mathRound(plotX), plotY, chart.inverted); // #1858 + // only draw the point if y is defined - if (plotY !== UNDEFINED && !isNaN(plotY)) { + if (enabled && plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { // shortcuts pointAttr = point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE]; radius = pointAttr.r; - symbol = pick(point.marker && point.marker.symbol, series.symbol); + symbol = pick(pointMarkerOptions.symbol, series.symbol); isImage = symbol.indexOf('url') === 0; if (graphic) { // update - graphic.animate(extend({ - x: plotX - radius, - y: plotY - radius - }, graphic.symbolName ? { // don't apply to image symbols #507 - width: 2 * radius, - height: 2 * radius - } : {})); - } else if (radius > 0 || isImage) { - point.graphic = chart.renderer.symbol( + graphic + .attr({ // Since the marker group isn't clipped, each individual marker must be toggled + visibility: isInside ? (hasSVG ? 'inherit' : VISIBLE) : HIDDEN + }) + .animate(extend({ + x: plotX - radius, + y: plotY - radius + }, graphic.symbolName ? { // don't apply to image symbols #507 + width: 2 * radius, + height: 2 * radius + } : {})); + } else if (isInside && (radius > 0 || isImage)) { + point.graphic = graphic = chart.renderer.symbol( symbol, plotX - radius, plotY - radius, @@ -12296,8 +13549,11 @@ 2 * radius ) .attr(pointAttr) - .add(series.group); + .add(markerGroup); } + + } else if (graphic) { + point.graphic = graphic.destroy(); // #1269 } } } @@ -12338,7 +13594,8 @@ */ getAttribs: function () { var series = this, - normalOptions = defaultPlotOptions[series.type].marker ? series.options.marker : series.options, + seriesOptions = series.options, + normalOptions = defaultPlotOptions[series.type].marker ? seriesOptions.marker : seriesOptions, stateOptions = normalOptions.states, stateOptionsHover = stateOptions[HOVER_STATE], pointStateOptionsHover, @@ -12354,15 +13611,16 @@ pointAttr, pointAttrToOptions = series.pointAttrToOptions, hasPointSpecificOptions, + negativeColor = seriesOptions.negativeColor, key; // series type specific modifications - if (series.options.marker) { // line, spline, area, areaspline, scatter + if (seriesOptions.marker) { // line, spline, area, areaspline, scatter // if no hover radius is given, default to normal radius + 2 stateOptionsHover.radius = stateOptionsHover.radius || normalOptions.radius + 2; stateOptionsHover.lineWidth = stateOptionsHover.lineWidth || normalOptions.lineWidth + 1; - + } else { // column, bar, pie // if no hover color is given, brighten the normal color @@ -12394,8 +13652,13 @@ if (normalOptions && normalOptions.enabled === false) { normalOptions.radius = 0; } - hasPointSpecificOptions = false; - + + if (point.negative && negativeColor) { + point.color = point.fillColor = negativeColor; + } + + hasPointSpecificOptions = seriesOptions.colorByPoint || point.color; // #868 + // check if the point has specific visual options if (point.options) { for (key in pointAttrToOptions) { @@ -12405,27 +13668,28 @@ } } - - // a specific marker config object is defined for the individual point: // create it's own attribute collection if (hasPointSpecificOptions) { - + normalOptions = normalOptions || {}; pointAttr = []; stateOptions = normalOptions.states || {}; // reassign for individual point pointStateOptionsHover = stateOptions[HOVER_STATE] = stateOptions[HOVER_STATE] || {}; - // if no hover color is given, brighten the normal color - if (!series.options.marker) { // column, bar, point + // Handle colors for column and pies + if (!seriesOptions.marker) { // column, bar, point + // if no hover color is given, brighten the normal color pointStateOptionsHover.color = - Color(pointStateOptionsHover.color || point.options.color) + Color(pointStateOptionsHover.color || point.color) .brighten(pointStateOptionsHover.brightness || stateOptionsHover.brightness).get(); } // normal point state inherits series wide normal state - pointAttr[NORMAL_STATE] = series.convertAttribs(normalOptions, seriesPointAttr[NORMAL_STATE]); + pointAttr[NORMAL_STATE] = series.convertAttribs(extend({ + color: point.color // #868 + }, normalOptions), seriesPointAttr[NORMAL_STATE]); // inherit from point normal and series hover pointAttr[HOVER_STATE] = series.convertAttribs( @@ -12433,6 +13697,7 @@ seriesPointAttr[HOVER_STATE], pointAttr[NORMAL_STATE] ); + // inherit from point normal and series hover pointAttr[SELECT_STATE] = series.convertAttribs( stateOptions[SELECT_STATE], @@ -12440,6 +13705,11 @@ pointAttr[NORMAL_STATE] ); + // Force the fill to negativeColor on markers + if (point.negative && seriesOptions.marker && negativeColor) { + pointAttr[NORMAL_STATE].fill = pointAttr[HOVER_STATE].fill = pointAttr[SELECT_STATE].fill = + series.convertAttribs({ fillColor: negativeColor }).fill; + } // no marker config object is created: copy a reference to the series-wide @@ -12453,7 +13723,33 @@ } }, + /** + * Update the series with a new set of options + */ + update: function (newOptions, redraw) { + var chart = this.chart, + // must use user options when changing type because this.options is merged + // in with type specific plotOptions + oldOptions = this.userOptions, + oldType = this.type; + + // Do the merge, with some forced options + newOptions = merge(oldOptions, { + animation: false, + index: this.index, + pointStart: this.xData[0] // when updating after addPoint + }, newOptions); + + // Destroy the series and reinsert methods from the type prototype + this.remove(false); + extend(this, seriesTypes[newOptions.type || oldType].prototype); + + this.init(chart, newOptions); + if (pick(redraw, true)) { + chart.redraw(false); + } + }, /** * Clear DOM objects and free up memory @@ -12461,7 +13757,6 @@ destroy: function () { var series = this, chart = series.chart, - seriesClipRect = series.clipRect, issue134 = /AppleWebKit\/533/.test(userAgent), destroy, i, @@ -12481,7 +13776,7 @@ axis = series[AXIS]; if (axis) { erase(axis.series, series); - axis.isDirty = true; + axis.isDirty = axis.forceRedraw = true; } }); @@ -12500,14 +13795,12 @@ } series.points = null; - // If this series clipRect is not the global one (which is removed on chart.destroy) we - // destroy it here. - if (seriesClipRect && seriesClipRect !== chart.clipRect) { - series.clipRect = seriesClipRect.destroy(); - } + // Clear the animation timeout if we are destroying the series during initial animation + clearTimeout(series.animationTimeout); // destroy all SVGElements associated to the series - each(['area', 'graph', 'dataLabelsGroup', 'group', 'tracker', 'trackerGroup'], function (prop) { + each(['area', 'graph', 'dataLabelsGroup', 'group', 'markerGroup', 'tracker', + 'graphNeg', 'areaNeg', 'posClip', 'negClip'], function (prop) { if (series[prop]) { // issue 134 workaround @@ -12538,134 +13831,65 @@ var series = this, seriesOptions = series.options, - options = seriesOptions.dataLabels; + options = seriesOptions.dataLabels, + points = series.points, + pointOptions, + generalOptions, + str, + dataLabelsGroup; if (options.enabled || series._hasPointLabels) { - var x, - y, - points = series.points, - pointOptions, - generalOptions, - str, - dataLabelsGroup = series.dataLabelsGroup, - chart = series.chart, - xAxis = series.xAxis, - groupLeft = xAxis ? xAxis.left : chart.plotLeft, - yAxis = series.yAxis, - groupTop = yAxis ? yAxis.top : chart.plotTop, - renderer = chart.renderer, - inverted = chart.inverted, - seriesType = series.type, - stacking = seriesOptions.stacking, - isBarLike = seriesType === 'column' || seriesType === 'bar', - vAlignIsNull = options.verticalAlign === null, - yIsNull = options.y === null, - fontMetrics = renderer.fontMetrics(options.style.fontSize), // height and baseline - fontLineHeight = fontMetrics.h, - fontBaseline = fontMetrics.b, - dataLabel, - enabled; - - if (isBarLike) { - var defaultYs = { - top: fontBaseline, - middle: fontBaseline - fontLineHeight / 2, - bottom: -fontLineHeight + fontBaseline - }; - if (stacking) { - // In stacked series the default label placement is inside the bars - if (vAlignIsNull) { - options = merge(options, {verticalAlign: 'middle'}); - } - - // If no y delta is specified, try to create a good default - if (yIsNull) { - options = merge(options, { y: defaultYs[options.verticalAlign]}); - } - } else { - // In non stacked series the default label placement is on top of the bars - if (vAlignIsNull) { - options = merge(options, {verticalAlign: 'top'}); - - // If no y delta is specified, try to create a good default (like default bar) - } else if (yIsNull) { - options = merge(options, { y: defaultYs[options.verticalAlign]}); - } - - } + + // Process default alignment of data labels for columns + if (series.dlProcessOptions) { + series.dlProcessOptions(options); } - - // create a separate group for the data labels to avoid rotation - if (!dataLabelsGroup) { - dataLabelsGroup = series.dataLabelsGroup = - renderer.g('data-labels') - .attr({ - visibility: series.visible ? VISIBLE : HIDDEN, - zIndex: 6 - }) - .translate(groupLeft, groupTop) - .add(); - } else { - dataLabelsGroup.translate(groupLeft, groupTop); - } + // Create a separate group for the data labels to avoid rotation + dataLabelsGroup = series.plotGroup( + 'dataLabelsGroup', + 'data-labels', + series.visible ? VISIBLE : HIDDEN, + options.zIndex || 6 + ); - // make the labels for each point + // Make the labels for each point generalOptions = options; each(points, function (point) { - dataLabel = point.dataLabel; + var enabled, + dataLabel = point.dataLabel, + labelConfig, + attr, + name, + rotation, + connector = point.connector, + isNew = true; + + // Determine if each data label is enabled + pointOptions = point.options && point.options.dataLabels; + enabled = generalOptions.enabled || (pointOptions && pointOptions.enabled); - // Merge in individual options from point - options = generalOptions; // reset changes from previous points - pointOptions = point.options; - if (pointOptions && pointOptions.dataLabels) { - options = merge(options, pointOptions.dataLabels); - } - enabled = options.enabled; - - // Get the positions - if (enabled) { - var plotX = (point.barX && point.barX + point.barW / 2) || pick(point.plotX, -999), - plotY = pick(point.plotY, -999), - - // if options.y is null, which happens by default on column charts, set the position - // above or below the column depending on the threshold - individualYDelta = options.y === null ? - (point.y >= seriesOptions.threshold ? - -fontLineHeight + fontBaseline : // below the threshold - fontBaseline) : // above the threshold - options.y; - - x = (inverted ? chart.plotWidth - plotY : plotX) + options.x; - y = mathRound((inverted ? chart.plotHeight - plotX : plotY) + individualYDelta); - - } // If the point is outside the plot area, destroy it. #678, #820 - if (dataLabel && series.isCartesian && (!chart.isInsidePlot(x, y) || !enabled)) { + if (dataLabel && !enabled) { point.dataLabel = dataLabel.destroy(); // Individual labels are disabled if the are explicitly disabled // in the point options, or if they fall outside the plot area. } else if (enabled) { - var align = options.align, - attr, - name; + rotation = options.rotation; + + // Create individual options structure that can be extended without + // affecting others + options = merge(generalOptions, pointOptions); // Get the string - str = options.formatter.call(point.getLabelConfig(), options); - - // in columns, align the string to the column - if (seriesType === 'column') { - x += { left: -1, right: 1 }[align] * point.barW / 2 || 0; - } - - if (!stacking && inverted && point.y < 0) { - align = 'right'; - x -= 10; - } + labelConfig = point.getLabelConfig(); + str = options.format ? + format(options.format, labelConfig) : + options.formatter.call(labelConfig, options); // Determine the color options.style.color = pick(options.color, options.style.color, series.color, 'black'); @@ -12673,23 +13897,30 @@ // update existing label if (dataLabel) { - // vertically centered - dataLabel - .attr({ - text: str - }).animate({ - x: x, - y: y - }); + + if (defined(str)) { + dataLabel + .attr({ + text: str + }); + isNew = false; + + } else { // #1437 - the label is shown conditionally + point.dataLabel = dataLabel = dataLabel.destroy(); + if (connector) { + point.connector = connector.destroy(); + } + } + // create new label } else if (defined(str)) { attr = { - align: align, + //align: align, fill: options.backgroundColor, stroke: options.borderColor, 'stroke-width': options.borderWidth, r: options.borderRadius || 0, - rotation: options.rotation, + rotation: rotation, padding: options.padding, zIndex: 1 }; @@ -12700,52 +13931,92 @@ } } - dataLabel = point.dataLabel = renderer[options.rotation ? 'text' : 'label']( // labels don't support rotation + dataLabel = point.dataLabel = series.chart.renderer[rotation ? 'text' : 'label']( // labels don't support rotation str, - x, - y, + 0, + -999, null, null, null, - options.useHTML, - true // baseline for backwards compat + options.useHTML ) .attr(attr) .css(options.style) .add(dataLabelsGroup) .shadow(options.shadow); - } - - if (isBarLike && seriesOptions.stacking && dataLabel) { - var barX = point.barX, - barY = point.barY, - barW = point.barW, - barH = point.barH; - - dataLabel.align(options, null, - { - x: inverted ? chart.plotWidth - barY - barH : barX, - y: inverted ? chart.plotHeight - barX - barW : barY, - width: inverted ? barH : barW, - height: inverted ? barW : barH - }); + } - + if (dataLabel) { + // Now the data label is created and placed at 0,0, so we need to align it + series.alignDataLabel(point, dataLabel, options, null, isNew); + } } }); } }, /** + * Align each individual data label + */ + alignDataLabel: function (point, dataLabel, options, alignTo, isNew) { + var chart = this.chart, + inverted = chart.inverted, + plotX = pick(point.plotX, -999), + plotY = pick(point.plotY, -999), + bBox = dataLabel.getBBox(), + alignAttr; // the final position; + + // The alignment box is a singular point + alignTo = extend({ + x: inverted ? chart.plotWidth - plotY : plotX, + y: mathRound(inverted ? chart.plotHeight - plotX : plotY), + width: 0, + height: 0 + }, alignTo); + + // Add the text size for alignment calculation + extend(options, { + width: bBox.width, + height: bBox.height + }); + + // Allow a hook for changing alignment in the last moment, then do the alignment + if (options.rotation) { // Fancy box alignment isn't supported for rotated text + alignAttr = { + align: options.align, + x: alignTo.x + options.x + alignTo.width / 2, + y: alignTo.y + options.y + alignTo.height / 2 + }; + dataLabel[isNew ? 'attr' : 'animate'](alignAttr); + } else { + dataLabel.align(options, null, alignTo); + alignAttr = dataLabel.alignAttr; + } + + // Show or hide based on the final aligned position + dataLabel.attr({ + visibility: options.crop === false || /*chart.isInsidePlot(alignAttr.x, alignAttr.y) || */chart.isInsidePlot(plotX, plotY, inverted) ? + (chart.renderer.isSVG ? 'inherit' : VISIBLE) : + HIDDEN + }); + + }, + + /** * Return the graph path of a segment */ getSegmentPath: function (segment) { var series = this, - segmentPath = []; - + segmentPath = [], + step = series.options.step; + // build the segment line each(segment, function (point, i) { + + var plotX = point.plotX, + plotY = point.plotY, + lastPoint; if (series.getPointSpline) { // generate the spline as defined in the SplineSeries object segmentPath.push.apply(segmentPath, series.getPointSpline(segment, point, i)); @@ -12756,12 +14027,28 @@ segmentPath.push(i ? L : M); // step line? - if (i && series.options.step) { - var lastPoint = segment[i - 1]; - segmentPath.push( - point.plotX, - lastPoint.plotY - ); + if (step && i) { + lastPoint = segment[i - 1]; + if (step === 'right') { + segmentPath.push( + lastPoint.plotX, + plotY + ); + + } else if (step === 'center') { + segmentPath.push( + (lastPoint.plotX + plotX) / 2, + lastPoint.plotY, + (lastPoint.plotX + plotX) / 2, + plotY + ); + + } else { + segmentPath.push( + plotX, + lastPoint.plotY + ); + } } // normal line to next point @@ -12776,24 +14063,15 @@ }, /** - * Draw the actual graph + * Get the graph path */ - drawGraph: function () { + getGraphPath: function () { var series = this, - options = series.options, - chart = series.chart, - graph = series.graph, graphPath = [], - group = series.group, - color = options.lineColor || series.color, - lineWidth = options.lineWidth, - dashStyle = options.dashStyle, segmentPath, - renderer = chart.renderer, - singlePoints = [], // used in drawTracker - attribs; + singlePoints = []; // used in drawTracker - // divide into segments and build graph and area paths + // Divide into segments and build graph and area paths each(series.segments, function (segment) { segmentPath = series.getSegmentPath(segment); @@ -12806,39 +14084,149 @@ } }); - // used in drawTracker: - series.graphPath = graphPath; + // Record it for use in drawGraph and drawTracker, and return graphPath series.singlePoints = singlePoints; - + series.graphPath = graphPath; + + return graphPath; + + }, + + /** + * Draw the actual graph + */ + drawGraph: function () { + var series = this, + options = this.options, + props = [['graph', options.lineColor || this.color]], + lineWidth = options.lineWidth, + dashStyle = options.dashStyle, + graphPath = this.getGraphPath(), + negativeColor = options.negativeColor; + + if (negativeColor) { + props.push(['graphNeg', negativeColor]); + } + // draw the graph - if (graph) { - stop(graph); // cancel running animations, #459 - graph.animate({ d: graphPath }); - - } else { - if (lineWidth) { + each(props, function (prop, i) { + var graphKey = prop[0], + graph = series[graphKey], + attribs; + + if (graph) { + stop(graph); // cancel running animations, #459 + graph.animate({ d: graphPath }); + + } else if (lineWidth && graphPath.length) { // #1487 attribs = { - stroke: color, - 'stroke-width': lineWidth + stroke: prop[1], + 'stroke-width': lineWidth, + zIndex: 1 // #1069 }; if (dashStyle) { attribs.dashstyle = dashStyle; } - series.graph = renderer.path(graphPath) - .attr(attribs).add(group).shadow(options.shadow); + series[graphKey] = series.chart.renderer.path(graphPath) + .attr(attribs) + .add(series.group) + .shadow(!i && options.shadow); } - } + }); + }, + + /** + * Clip the graphs into the positive and negative coloured graphs + */ + clipNeg: function () { + var options = this.options, + chart = this.chart, + renderer = chart.renderer, + negativeColor = options.negativeColor, + translatedThreshold, + posAttr, + negAttr, + graph = this.graph, + area = this.area, + posClip = this.posClip, + negClip = this.negClip, + chartWidth = chart.chartWidth, + chartHeight = chart.chartHeight, + chartSizeMax = mathMax(chartWidth, chartHeight), + above, + below; + + if (negativeColor && (graph || area)) { + translatedThreshold = mathCeil(this.yAxis.len - this.yAxis.translate(options.threshold || 0)); + above = { + x: 0, + y: 0, + width: chartSizeMax, + height: translatedThreshold + }; + below = { + x: 0, + y: translatedThreshold, + width: chartSizeMax, + height: chartSizeMax - translatedThreshold + }; + + if (chart.inverted && renderer.isVML) { + above = { + x: chart.plotWidth - translatedThreshold - chart.plotLeft, + y: 0, + width: chartWidth, + height: chartHeight + }; + below = { + x: translatedThreshold + chart.plotLeft - chartWidth, + y: 0, + width: chart.plotLeft + translatedThreshold, + height: chartWidth + }; + } + + if (this.yAxis.reversed) { + posAttr = below; + negAttr = above; + } else { + posAttr = above; + negAttr = below; + } + + if (posClip) { // update + posClip.animate(posAttr); + negClip.animate(negAttr); + } else { + + this.posClip = posClip = renderer.clipRect(posAttr); + this.negClip = negClip = renderer.clipRect(negAttr); + + if (graph) { + graph.clip(posClip); + this.graphNeg.clip(negClip); + } + + if (area) { + area.clip(posClip); + this.areaNeg.clip(negClip); + } + } + } }, /** - * Initialize and perform group inversion on series.group and series.trackerGroup + * Initialize and perform group inversion on series.group and series.markerGroup */ invertGroups: function () { var series = this, - group = series.group, - trackerGroup = series.trackerGroup, chart = series.chart; + + // Pie, go away (#1736) + if (!series.xAxis) { + return; + } // A fixed size is needed for inversion to work function setInvert() { @@ -12847,13 +14235,11 @@ height: series.xAxis.len }; - // Set the series.group size - group.attr(size).invert(); - - // Set the tracker group size - if (trackerGroup) { - trackerGroup.attr(size).invert(); - } + each(['group', 'markerGroup'], function (groupName) { + if (series[groupName]) { + series[groupName].attr(size).invert(); + } + }); } addEvent(chart, 'resize', setInvert); // do it on resize @@ -12869,24 +14255,36 @@ }, /** - * Create the series group + * General abstraction for creating plot groups like series.group, series.dataLabelsGroup and + * series.markerGroup. On subsequent calls, the group will only be adjusted to the updated plot size. */ - createGroup: function () { + plotGroup: function (prop, name, visibility, zIndex, parent) { + var group = this[prop], + isNew = !group, + chart = this.chart, + xAxis = this.xAxis, + yAxis = this.yAxis; - var chart = this.chart, - group = this.group = chart.renderer.g('series'); - - group.attr({ - visibility: this.visible ? VISIBLE : HIDDEN, - zIndex: this.options.zIndex - }) - .translate(this.xAxis.left, this.yAxis.top) - .add(chart.seriesGroup); + // Generate it on first call + if (isNew) { + this[prop] = group = chart.renderer.g(name) + .attr({ + visibility: visibility, + zIndex: zIndex || 0.1 // IE8 needs this + }) + .add(parent); + } + // Place it on first and subsequent (redraw) calls + group[isNew ? 'attr' : 'animate']({ + translateX: xAxis ? xAxis.left : chart.plotLeft, + translateY: yAxis ? yAxis.top : chart.plotTop, + scaleX: 1, // #1623 + scaleY: 1 + }); + return group; - // Only run this once - this.createGroup = noop; }, - + /** * Render the graph and markers */ @@ -12895,37 +14293,32 @@ chart = series.chart, group, options = series.options, - doClip = options.clip !== false, animation = options.animation, - doAnimation = animation && series.animate, - duration = doAnimation ? (animation && animation.duration) || 500 : 0, - clipRect = series.clipRect, - renderer = chart.renderer; - - - // Add plot area clipping rectangle. If this is before chart.hasRendered, - // create one shared clipRect. - - // Todo: since creating the clip property, the clipRect is created but - // never used when clip is false. A better way would be that the animation - // would run, then the clipRect destroyed. - if (!clipRect) { - clipRect = series.clipRect = !chart.hasRendered && chart.clipRect ? - chart.clipRect : - renderer.clipRect(0, 0, chart.plotSizeX, chart.plotSizeY + 1); - if (!chart.clipRect) { - chart.clipRect = clipRect; - } - } + doAnimation = animation && !!series.animate && + chart.renderer.isSVG, // this animation doesn't work in IE8 quirks when the group div is hidden, + // and looks bad in other oldIE + visibility = series.visible ? VISIBLE : HIDDEN, + zIndex = options.zIndex, + hasRendered = series.hasRendered, + chartSeriesGroup = chart.seriesGroup; - // the group - series.createGroup(); - group = series.group; + group = series.plotGroup( + 'group', + 'series', + visibility, + zIndex, + chartSeriesGroup + ); + series.markerGroup = series.plotGroup( + 'markerGroup', + 'markers', + visibility, + zIndex, + chartSeriesGroup + ); - series.drawDataLabels(); - // initiate the animation if (doAnimation) { series.animate(true); @@ -12934,14 +14327,22 @@ // cache attributes for shapes series.getAttribs(); + // SVGRenderer needs to know this before drawing elements (#1089, #1795) + group.inverted = series.isCartesian ? chart.inverted : false; + // draw the graph if any if (series.drawGraph) { series.drawGraph(); + series.clipNeg(); } + // draw the data labels (inn pies they go before the points) + series.drawDataLabels(); + // draw the points series.drawPoints(); + // draw the mouse tracking area if (series.options.enableMouseTracking !== false) { series.drawTracker(); @@ -12952,37 +14353,23 @@ series.invertGroups(); } - // Do the initial clipping. This must be done after inverting for VML. - if (doClip && !series.hasRendered) { - group.clip(clipRect); - if (series.trackerGroup) { - series.trackerGroup.clip(chart.clipRect); - } + // Initial clipping, must be defined after inverting groups for VML + if (options.clip !== false && !series.sharedClipKey && !hasRendered) { + group.clip(chart.clipRect); } - - // run the animation + // Run the animation if (doAnimation) { series.animate(); + } else if (!hasRendered) { + series.afterAnimate(); } - // finish the individual clipRect - setTimeout(function () { - clipRect.isAnimating = false; - group = series.group; // can be destroyed during the timeout - if (group && clipRect !== chart.clipRect && clipRect.renderer) { - if (doClip) { - group.clip((series.clipRect = chart.clipRect)); - } - clipRect.destroy(); - } - }, duration); - series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see // (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see series.hasRendered = true; }, - + /** * Redraw the series after an update in the axes. */ @@ -12990,7 +14377,9 @@ var series = this, chart = series.chart, wasDirtyData = series.isDirtyData, // cache it here as it is set to false in render, but used after - group = series.group; + group = series.group, + xAxis = series.xAxis, + yAxis = series.yAxis; // reposition on resize if (group) { @@ -13002,8 +14391,8 @@ } group.animate({ - translateX: series.xAxis.left, - translateY: series.yAxis.top + translateX: pick(xAxis && xAxis.left, chart.plotLeft), + translateY: pick(yAxis && yAxis.top, chart.plotTop) }); } @@ -13023,8 +14412,10 @@ var series = this, options = series.options, graph = series.graph, + graphNeg = series.graphNeg, stateOptions = options.states, - lineWidth = options.lineWidth; + lineWidth = options.lineWidth, + attribs; state = state || NORMAL_STATE; @@ -13040,9 +14431,14 @@ } if (graph && !graph.dashstyle) { // hover is turned off for dashed lines in VML - graph.attr({ // use attr because animate will cause any other animation on the graph to stop + attribs = { 'stroke-width': lineWidth - }, state ? 0 : 500); + }; + // use attr because animate will cause any other animation on the graph to stop + graph.attr(attribs); + if (graphNeg) { + graphNeg.attr(attribs); + } } } }, @@ -13057,43 +14453,28 @@ var series = this, chart = series.chart, legendItem = series.legendItem, - seriesGroup = series.group, - seriesTracker = series.tracker, - dataLabelsGroup = series.dataLabelsGroup, showOrHide, - i, - points = series.points, - point, ignoreHiddenSeries = chart.options.chart.ignoreHiddenSeries, oldVisibility = series.visible; // if called without an argument, toggle visibility - series.visible = vis = vis === UNDEFINED ? !oldVisibility : vis; + series.visible = vis = series.userOptions.visible = vis === UNDEFINED ? !oldVisibility : vis; showOrHide = vis ? 'show' : 'hide'; - // show or hide series - if (seriesGroup) { // pies don't have one - seriesGroup[showOrHide](); - } - - // show or hide trackers - if (seriesTracker) { - seriesTracker[showOrHide](); - } else if (points) { - i = points.length; - while (i--) { - point = points[i]; - if (point.tracker) { - point.tracker[showOrHide](); - } + // show or hide elements + each(['group', 'dataLabelsGroup', 'markerGroup', 'tracker'], function (key) { + if (series[key]) { + series[key][showOrHide](); } - } - + }); - if (dataLabelsGroup) { - dataLabelsGroup[showOrHide](); + + // hide tooltip (#1361) + if (chart.hoverSeries === series) { + series.onMouseOut(); } + if (legendItem) { chart.legend.colorizeItem(series, vis); } @@ -13110,6 +14491,11 @@ }); } + // show or hide linked series + each(series.linkedSeries, function (otherSeries) { + otherSeries.setVisible(vis, false); + }); + if (ignoreHiddenSeries) { chart.isDirtyBox = true; } @@ -13154,33 +14540,6 @@ }, /** - * Create a group that holds the tracking object or objects. This allows for - * individual clipping and placement of each series tracker. - */ - drawTrackerGroup: function () { - var trackerGroup = this.trackerGroup, - chart = this.chart; - - if (this.isCartesian) { - - // Generate it on first call - if (!trackerGroup) { - this.trackerGroup = trackerGroup = chart.renderer.g() - .attr({ - zIndex: this.options.zIndex || 1 - }) - .add(chart.trackerGroup); - - } - // Place it on first and subsequent (redraw) calls - trackerGroup.translate(this.xAxis.left, this.yAxis.top); - - } - - return trackerGroup; - }, - - /** * Draw the tracker object that sits above all data labels and markers to * track mouse events on the graph or points. For the line type charts * the tracker uses the same graphPath, but with a greater stroke width @@ -13193,15 +14552,20 @@ trackerPath = [].concat(trackByArea ? series.areaPath : series.graphPath), trackerPathLength = trackerPath.length, chart = series.chart, + pointer = chart.pointer, renderer = chart.renderer, snap = chart.options.tooltip.snap, tracker = series.tracker, cursor = options.cursor, css = cursor && { cursor: cursor }, singlePoints = series.singlePoints, - trackerGroup = series.drawTrackerGroup(), singlePoint, - i; + i, + onMouseOver = function () { + if (chart.hoverSeries !== series) { + series.onMouseOver(); + } + }; // Extend end points. A better way would be to use round linecaps, // but those are not clickable in VML. @@ -13232,27 +14596,25 @@ } else { // create - series.tracker = renderer.path(trackerPath) + series.tracker = tracker = renderer.path(trackerPath) .attr({ - isTracker: true, - 'stroke-linejoin': 'bevel', + 'class': PREFIX + 'tracker', + 'stroke-linejoin': 'round', // #1225 visibility: series.visible ? VISIBLE : HIDDEN, stroke: TRACKER_FILL, fill: trackByArea ? TRACKER_FILL : NONE, - 'stroke-width' : options.lineWidth + (trackByArea ? 0 : 2 * snap) - }) - .on(hasTouch ? 'touchstart' : 'mouseover', function () { - if (chart.hoverSeries !== series) { - series.onMouseOver(); - } - }) - .on('mouseout', function () { - if (!options.stickyTracking) { - series.onMouseOut(); - } + 'stroke-width' : options.lineWidth + (trackByArea ? 0 : 2 * snap), + zIndex: 2 }) + .addClass(PREFIX + 'tracker') + .on('mouseover', onMouseOver) + .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); }) .css(css) - .add(trackerGroup); + .add(series.markerGroup); + + if (hasTouch) { + tracker.on('touchstart', onMouseOver); + } } } @@ -13284,6 +14646,73 @@ type: 'area', /** + * For stacks, don't split segments on null values. Instead, draw null values with + * no marker. Also insert dummy points for any X position that exists in other series + * in the stack. + */ + getSegments: function () { + var segments = [], + segment = [], + keys = [], + xAxis = this.xAxis, + yAxis = this.yAxis, + stack = yAxis.stacks[this.stackKey], + pointMap = {}, + plotX, + plotY, + points = this.points, + i, + x; + + if (this.options.stacking && !this.cropped) { // cropped causes artefacts in Stock, and perf issue + // Create a map where we can quickly look up the points by their X value. + for (i = 0; i < points.length; i++) { + pointMap[points[i].x] = points[i]; + } + + // Sort the keys (#1651) + for (x in stack) { + keys.push(+x); + } + keys.sort(function (a, b) { + return a - b; + }); + + each(keys, function (x) { + // The point exists, push it to the segment + if (pointMap[x]) { + segment.push(pointMap[x]); + + // There is no point for this X value in this series, so we + // insert a dummy point in order for the areas to be drawn + // correctly. + } else { + plotX = xAxis.translate(x); + plotY = yAxis.toPixels(stack[x].cum, true); + segment.push({ + y: null, + plotX: plotX, + clientX: plotX, + plotY: plotY, + yBottom: plotY, + onMouseOver: noop + }); + } + }); + + if (segment.length) { + segments.push(segment); + } + + } else { + Series.prototype.getSegments.call(this); + segments = this.segments; + } + + this.segments = segments; + }, + + /** * Extend the base Series getSegmentPath method by adding the path for the area. * This path is pushed to the series.areaPath property. */ @@ -13293,13 +14722,12 @@ areaSegmentPath = [].concat(segmentPath), // work on a copy for the area path i, options = this.options, - segLength = segmentPath.length, - translatedThreshold = this.yAxis.getThreshold(options.threshold); + segLength = segmentPath.length; if (segLength === 3) { // for animation from 1 to two points areaSegmentPath.push(L, segmentPath[1], segmentPath[2]); } - if (options.stacking && this.type !== 'areaspline') { + if (options.stacking && !this.closedStacks) { // Follow stack back. Todo: implement areaspline. A general solution could be to // reverse the entire graphPath of the previous series, though may be hard with @@ -13315,14 +14743,7 @@ } } else { // follow zero line back - areaSegmentPath.push( - L, - segment[segment.length - 1].plotX, - translatedThreshold, - L, - segment[0].plotX, - translatedThreshold - ); + this.closeSegment(areaSegmentPath, segment); } this.areaPath = this.areaPath.concat(areaSegmentPath); @@ -13330,6 +14751,22 @@ }, /** + * Extendable method to close the segment path of an area. This is overridden in polar + * charts. + */ + closeSegment: function (path, segment) { + var translatedThreshold = this.yAxis.getThreshold(this.options.threshold); + path.push( + L, + segment[segment.length - 1].plotX, + translatedThreshold, + L, + segment[0].plotX, + translatedThreshold + ); + }, + + /** * Draw the graph and the underlying area. This method calls the Series base * function and adds the area. The areaPath is calculated in the getSegmentPath * method called from Series.prototype.drawGraph. @@ -13343,23 +14780,35 @@ Series.prototype.drawGraph.apply(this); // Define local variables - var areaPath = this.areaPath, + var series = this, + areaPath = this.areaPath, options = this.options, - area = this.area; + negativeColor = options.negativeColor, + props = [['area', this.color, options.fillColor]]; // area name, main color, fill color - // Create or update the area - if (area) { // update - area.animate({ d: areaPath }); - - } else { // create - this.area = this.chart.renderer.path(areaPath) - .attr({ - fill: pick( - options.fillColor, - Color(this.color).setOpacity(options.fillOpacity || 0.75).get() - ) - }).add(this.group); + if (negativeColor) { + props.push(['areaNeg', options.negativeColor, options.negativeFillColor]); } + + each(props, function (prop) { + var areaKey = prop[0], + area = series[areaKey]; + + // Create or update the area + if (area) { // update + area.animate({ d: areaPath }); + + } else { // create + series[areaKey] = series.chart.renderer.path(areaPath) + .attr({ + fill: pick( + prop[2], + Color(prop[1]).setOpacity(options.fillOpacity || 0.75).get() + ), + zIndex: 0 // #1069 + }).add(series.group); + } + }); }, /** @@ -13395,7 +14844,7 @@ type: 'spline', /** - * Draw the actual graph + * Get the spline segment from a given point's previous neighbour to the given point */ getPointSpline: function (segment, point, i) { var smoothing = 1.5, // 1 means control points midway between points, 2 means 1/3 from the point, 3 is 1/4 etc @@ -13411,7 +14860,8 @@ ret; // find control points - if (i && i < segment.length - 1) { + if (lastPoint && nextPoint) { + var lastX = lastPoint.plotX, lastY = lastPoint.plotY, nextX = nextPoint.plotX, @@ -13452,6 +14902,40 @@ point.rightContY = rightContY; } + + // Visualize control points for debugging + /* + if (leftContX) { + this.chart.renderer.circle(leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, 2) + .attr({ + stroke: 'red', + 'stroke-width': 1, + fill: 'none' + }) + .add(); + this.chart.renderer.path(['M', leftContX + this.chart.plotLeft, leftContY + this.chart.plotTop, + 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop]) + .attr({ + stroke: 'red', + 'stroke-width': 1 + }) + .add(); + this.chart.renderer.circle(rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, 2) + .attr({ + stroke: 'green', + 'stroke-width': 1, + fill: 'none' + }) + .add(); + this.chart.renderer.path(['M', rightContX + this.chart.plotLeft, rightContY + this.chart.plotTop, + 'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop]) + .attr({ + stroke: 'green', + 'stroke-width': 1 + }) + .add(); + } + */ // moveTo or lineTo if (!i) { @@ -13484,9 +14968,11 @@ var areaProto = AreaSeries.prototype, AreaSplineSeries = extendClass(SplineSeries, { type: 'areaspline', + closedStacks: true, // instead of following the previous graph back, follow the threshold back // Mix in methods from the area series getSegmentPath: areaProto.getSegmentPath, + closeSegment: areaProto.closeSegment, drawGraph: areaProto.drawGraph }); seriesTypes.areaspline = AreaSplineSeries; @@ -13500,6 +14986,7 @@ borderRadius: 0, //colorByPoint: undefined, groupPadding: 0.2, + //grouping: true, marker: null, // point options are specified in the base options pointPadding: 0.1, //pointWidth: null, @@ -13518,9 +15005,11 @@ } }, dataLabels: { - y: null, - verticalAlign: null + align: null, // auto + verticalAlign: null, // auto + y: null }, + stickyTracking: false, threshold: 0 }); @@ -13530,12 +15019,18 @@ var ColumnSeries = extendClass(Series, { type: 'column', tooltipOutsidePlot: true, + requireSorting: false, pointAttrToOptions: { // mapping between SVG attributes and the corresponding options stroke: 'borderColor', 'stroke-width': 'borderWidth', fill: 'color', r: 'borderRadius' }, + trackerGroups: ['group', 'dataLabelsGroup'], + + /** + * Initialize the series + */ init: function () { Series.prototype.init.apply(this, arguments); @@ -13554,47 +15049,49 @@ }, /** - * Translate each point to the plot area coordinate system and find shape positions + * Return the width and x offset of the columns adjusted for grouping, groupPadding, pointPadding, + * pointWidth etc. */ - translate: function () { + getColumnMetrics: function () { + var series = this, chart = series.chart, options = series.options, - stacking = options.stacking, - borderWidth = options.borderWidth, - columnCount = 0, - xAxis = series.xAxis, + xAxis = this.xAxis, reversedXAxis = xAxis.reversed, - stackGroups = {}, stackKey, - columnIndex; - - Series.prototype.translate.apply(series); + stackGroups = {}, + columnIndex, + columnCount = 0; // Get the total number of column type series. // This is called on every series. Consider moving this logic to a // chart.orderStacks() function and call it on init, addSeries and removeSeries - each(chart.series, function (otherSeries) { - if (otherSeries.type === series.type && otherSeries.visible && - series.options.group === otherSeries.options.group) { // used in Stock charts navigator series - if (otherSeries.options.stacking) { - stackKey = otherSeries.stackKey; - if (stackGroups[stackKey] === UNDEFINED) { - stackGroups[stackKey] = columnCount++; + if (options.grouping === false) { + columnCount = 1; + } else { + each(chart.series, function (otherSeries) { + var otherOptions = otherSeries.options; + if (otherSeries.type === series.type && otherSeries.visible && + series.options.group === otherOptions.group) { // used in Stock charts navigator series + if (otherOptions.stacking) { + stackKey = otherSeries.stackKey; + if (stackGroups[stackKey] === UNDEFINED) { + stackGroups[stackKey] = columnCount++; + } + columnIndex = stackGroups[stackKey]; + } else if (otherOptions.grouping !== false) { // #1162 + columnIndex = columnCount++; } - columnIndex = stackGroups[stackKey]; - } else { - columnIndex = columnCount++; + otherSeries.columnIndex = columnIndex; } - otherSeries.columnIndex = columnIndex; - } - }); + }); + } - // calculate the width and position of each column based on - // the number of column series in the plot, the groupPadding - // and the pointPadding options - var points = series.points, - categoryWidth = mathAbs(xAxis.transA) * (xAxis.ordinalSlope || options.pointRange || xAxis.closestPointRange || 1), + var categoryWidth = mathMin( + mathAbs(xAxis.transA) * (xAxis.ordinalSlope || options.pointRange || xAxis.closestPointRange || 1), + xAxis.len // #1535 + ), groupPadding = categoryWidth * options.groupPadding, groupWidth = categoryWidth - 2 * groupPadding, pointOffsetWidth = groupWidth / columnCount, @@ -13602,24 +15099,49 @@ pointPadding = defined(optionPointWidth) ? (pointOffsetWidth - optionPointWidth) / 2 : pointOffsetWidth * options.pointPadding, pointWidth = pick(optionPointWidth, pointOffsetWidth - 2 * pointPadding), // exact point width, used in polar charts - barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width - colIndex = (reversedXAxis ? columnCount - - series.columnIndex : series.columnIndex) || 0, + colIndex = (reversedXAxis ? + columnCount - (series.columnIndex || 0) : // #1251 + series.columnIndex) || 0, pointXOffset = pointPadding + (groupPadding + colIndex * pointOffsetWidth - (categoryWidth / 2)) * - (reversedXAxis ? -1 : 1), + (reversedXAxis ? -1 : 1); + + // Save it for reading in linked series (Error bars particularly) + return (series.columnMetrics = { + width: pointWidth, + offset: pointXOffset + }); + + }, + + /** + * Translate each point to the plot area coordinate system and find shape positions + */ + translate: function () { + var series = this, + chart = series.chart, + options = series.options, + stacking = options.stacking, + borderWidth = options.borderWidth, + yAxis = series.yAxis, threshold = options.threshold, - translatedThreshold = series.yAxis.getThreshold(threshold), - minPointLength = pick(options.minPointLength, 5); + translatedThreshold = series.translatedThreshold = yAxis.getThreshold(threshold), + minPointLength = pick(options.minPointLength, 5), + metrics = series.getColumnMetrics(), + pointWidth = metrics.width, + barW = mathCeil(mathMax(pointWidth, 1 + 2 * borderWidth)), // rounded and postprocessed for border width + pointXOffset = metrics.offset; + + Series.prototype.translate.apply(series); // record the new values - each(points, function (point) { - var plotY = point.plotY, + each(series.points, function (point) { + var plotY = mathMin(mathMax(-999, point.plotY), yAxis.len + 999), // Don't draw too far outside plot area (#1303) yBottom = pick(point.yBottom, translatedThreshold), barX = point.plotX + pointXOffset, barY = mathCeil(mathMin(plotY, yBottom)), barH = mathCeil(mathMax(plotY, yBottom) - barY), - stack = series.yAxis.stacks[(point.y < 0 ? '-' : '') + series.stackKey], + stack = yAxis.stacks[(point.y < 0 ? '-' : '') + series.stackKey], shapeArgs; // Record the offset'ed position and width of the bar to be able to align the stacking total correctly @@ -13634,17 +15156,12 @@ barY = mathAbs(barY - translatedThreshold) > minPointLength ? // stacked yBottom - minPointLength : // keep position - translatedThreshold - (plotY <= translatedThreshold ? minPointLength : 0); + translatedThreshold - (yAxis.translate(point.y, 0, 1, 0, 1) <= translatedThreshold ? minPointLength : 0); // use exact yAxis.translation (#1485) } } - extend(point, { - barX: barX, - barY: barY, - barW: barW, - barH: barH, - pointWidth: pointWidth - }); + point.barX = barX; + point.pointWidth = pointWidth; // create shape type and shape args that are reused in drawPoints and drawTracker point.shapeType = 'rect'; @@ -13655,17 +15172,11 @@ shapeArgs.height += 1; } - // make small columns responsive to mouse - point.trackerArgs = mathAbs(barH) < 3 && merge(point.shapeArgs, { - height: 6, - y: barY - 3 - }); }); }, - getSymbol: function () { - }, + getSymbol: noop, /** * Use a solid rectangle like the area series types @@ -13676,7 +15187,7 @@ /** * Columns have no graph */ - drawGraph: function () {}, + drawGraph: noop, /** * Draw the columns. For bars, the series.group is rotated, so the same coordinates @@ -13687,16 +15198,17 @@ var series = this, options = series.options, renderer = series.chart.renderer, - graphic, shapeArgs; // draw the columns each(series.points, function (point) { - var plotY = point.plotY; - if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { + var plotY = point.plotY, graphic = point.graphic; + + if (plotY !== UNDEFINED && !isNaN(plotY) && point.y !== null) { shapeArgs = point.shapeArgs; + if (graphic) { // update stop(graphic); graphic.animate(merge(shapeArgs)); @@ -13708,67 +15220,113 @@ .shadow(options.shadow, null, options.stacking && !options.borderRadius); } + } else if (graphic) { + point.graphic = graphic.destroy(); // #1269 } }); }, + /** - * Draw the individual tracker elements. - * This method is inherited by scatter and pie charts too. + * Add tracking event listener to the series group, so the point graphics + * themselves act as trackers */ drawTracker: function () { var series = this, - chart = series.chart, - renderer = chart.renderer, - shapeArgs, - tracker, - trackerLabel = +new Date(), - options = series.options, - cursor = options.cursor, + pointer = series.chart.pointer, + cursor = series.options.cursor, css = cursor && { cursor: cursor }, - trackerGroup = series.drawTrackerGroup(), - rel, - plotY, - validPlotY; - + onMouseOver = function (e) { + var target = e.target, + point; + + series.onMouseOver(); + + while (target && !point) { + point = target.point; + target = target.parentNode; + } + if (point !== UNDEFINED) { // undefined on graph in scatterchart + point.onMouseOver(e); + } + }; + + // Add reference to the point each(series.points, function (point) { - tracker = point.tracker; - shapeArgs = point.trackerArgs || point.shapeArgs; - plotY = point.plotY; - validPlotY = !series.isCartesian || (plotY !== UNDEFINED && !isNaN(plotY)); - delete shapeArgs.strokeWidth; - if (point.y !== null && validPlotY) { - if (tracker) {// update - tracker.attr(shapeArgs); + if (point.graphic) { + point.graphic.element.point = point; + } + if (point.dataLabel) { + point.dataLabel.element.point = point; + } + }); + // Add the event listeners, we need to do this only once + if (!series._hasTracking) { + each(series.trackerGroups, function (key) { + if (series[key]) { // we don't always have dataLabelsGroup + series[key] + .addClass(PREFIX + 'tracker') + .on('mouseover', onMouseOver) + .on('mouseout', function (e) { pointer.onTrackerMouseOut(e); }) + .css(css); + if (hasTouch) { + series[key].on('touchstart', onMouseOver); + } + } + }); + + } else { + series._hasTracking = true; + } + }, + + /** + * Override the basic data label alignment by adjusting for the position of the column + */ + alignDataLabel: function (point, dataLabel, options, alignTo, isNew) { + var chart = this.chart, + inverted = chart.inverted, + dlBox = point.dlBox || point.shapeArgs, // data label box for alignment + below = point.below || (point.plotY > pick(this.translatedThreshold, chart.plotSizeY)), + inside = pick(options.inside, !!this.options.stacking); // draw it inside the box? + + // Align to the column itself, or the top of it + if (dlBox) { // Area range uses this method but not alignTo + alignTo = merge(dlBox); + if (inverted) { + alignTo = { + x: chart.plotWidth - alignTo.y - alignTo.height, + y: chart.plotHeight - alignTo.x - alignTo.width, + width: alignTo.height, + height: alignTo.width + }; + } + + // Compute the alignment box + if (!inside) { + if (inverted) { + alignTo.x += below ? 0 : alignTo.width; + alignTo.width = 0; } else { - point.tracker = - renderer[point.shapeType](shapeArgs) - .attr({ - isTracker: trackerLabel, - fill: TRACKER_FILL, - visibility: series.visible ? VISIBLE : HIDDEN - }) - .on(hasTouch ? 'touchstart' : 'mouseover', function (event) { - rel = event.relatedTarget || event.fromElement; - if (chart.hoverSeries !== series && attr(rel, 'isTracker') !== trackerLabel) { - series.onMouseOver(); - } - point.onMouseOver(); - - }) - .on('mouseout', function (event) { - if (!options.stickyTracking) { - rel = event.relatedTarget || event.toElement; - if (attr(rel, 'isTracker') !== trackerLabel) { - series.onMouseOut(); - } - } - }) - .css(css) - .add(point.group || trackerGroup); // pies have point group - see issue #118 + alignTo.y += below ? alignTo.height : 0; + alignTo.height = 0; } } - }); + } + + // When alignment is undefined (typically columns and bars), display the individual + // point below or above the point depending on the threshold + options.align = pick( + options.align, + !inverted || inside ? 'center' : below ? 'right' : 'left' + ); + options.verticalAlign = pick( + options.verticalAlign, + inverted || inside ? 'middle' : below ? 'top' : 'bottom' + ); + + // Call the parent method + Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew); }, @@ -13778,47 +15336,35 @@ */ animate: function (init) { var series = this, - points = series.points, - options = series.options; - - if (!init) { // run the animation - /* - * Note: Ideally the animation should be initialized by calling - * series.group.hide(), and then calling series.group.show() - * after the animation was started. But this rendered the shadows - * invisible in IE8 standards mode. If the columns flicker on large - * datasets, this is the cause. - */ - - each(points, function (point) { - var graphic = point.graphic, - shapeArgs = point.shapeArgs, - yAxis = series.yAxis, - threshold = options.threshold; - - if (graphic) { - // start values - graphic.attr({ - height: 0, - y: defined(threshold) ? - yAxis.getThreshold(threshold) : - yAxis.translate(yAxis.getExtremes().min, 0, 1, 0, 1) - }); - - // animate - graphic.animate({ - height: shapeArgs.height, - y: shapeArgs.y - }, options.animation); + yAxis = this.yAxis, + options = series.options, + inverted = this.chart.inverted, + attr = {}, + translatedThreshold; + + if (hasSVG) { // VML is too slow anyway + if (init) { + attr.scaleY = 0.001; + translatedThreshold = mathMin(yAxis.pos + yAxis.len, mathMax(yAxis.pos, yAxis.toPixels(options.threshold))); + if (inverted) { + attr.translateX = translatedThreshold - yAxis.len; + } else { + attr.translateY = translatedThreshold; } - }); + series.group.attr(attr); + } else { // run the animation + + attr.scaleY = 1; + attr[inverted ? 'translateX' : 'translateY'] = yAxis.pos; + series.group.animate(attr, series.options.animation); - // delete this function to allow it only once - series.animate = null; + // delete this function to allow it only once + series.animate = null; + } } - }, + /** * Remove this series from the chart */ @@ -13843,14 +15389,7 @@ /** * Set the default options for bar */ -defaultPlotOptions.bar = merge(defaultPlotOptions.column, { - dataLabels: { - align: 'left', - x: 5, - y: null, - verticalAlign: 'middle' - } -}); +defaultPlotOptions.bar = merge(defaultPlotOptions.column); /** * The Bar series class */ @@ -13865,15 +15404,12 @@ */ defaultPlotOptions.scatter = merge(defaultSeriesOptions, { lineWidth: 0, - states: { - hover: { - lineWidth: 0 - } - }, tooltip: { headerFormat: '{series.name}
    ', - pointFormat: 'x: {point.x}
    y: {point.y}
    ' - } + pointFormat: 'x: {point.x}
    y: {point.y}
    ', + followPointer: true + }, + stickyTracking: false }); /** @@ -13882,68 +15418,13 @@ var ScatterSeries = extendClass(Series, { type: 'scatter', sorted: false, - /** - * Extend the base Series' translate method by adding shape type and - * arguments for the point trackers - */ - translate: function () { - var series = this; - - Series.prototype.translate.apply(series); - - each(series.points, function (point) { - point.shapeType = 'circle'; - point.shapeArgs = { - x: point.plotX, - y: point.plotY, - r: series.chart.options.tooltip.snap - }; - }); - }, - - /** - * Add tracking event listener to the series group, so the point graphics - * themselves act as trackers - */ - drawTracker: function () { - var series = this, - cursor = series.options.cursor, - css = cursor && { cursor: cursor }, - points = series.points, - i = points.length, - graphic; - - // Set an expando property for the point index, used below - while (i--) { - graphic = points[i].graphic; - if (graphic) { // doesn't exist for null points - graphic.element._i = i; - } - } - - // Add the event listeners, we need to do this only once - if (!series._hasTracking) { - series.group - .attr({ - isTracker: true - }) - .on(hasTouch ? 'touchstart' : 'mouseover', function (e) { - series.onMouseOver(); - if (e.target._i !== UNDEFINED) { // undefined on graph in scatterchart - points[e.target._i].onMouseOver(); - } - }) - .on('mouseout', function () { - if (!series.options.stickyTracking) { - series.onMouseOut(); - } - }) - .css(css); - } else { - series._hasTracking = true; - } + requireSorting: false, + noSharedTooltip: true, + trackerGroups: ['markerGroup'], - } + drawTracker: ColumnSeries.prototype.drawTracker, + + setTooltipPoints: noop }); seriesTypes.scatter = ScatterSeries; @@ -13953,7 +15434,8 @@ defaultPlotOptions.pie = merge(defaultSeriesOptions, { borderColor: '#FFFFFF', borderWidth: 1, - center: ['50%', '50%'], + center: [null, null], + clip: false, colorByPoint: true, // always true for pies dataLabels: { // align: null, @@ -13964,14 +15446,15 @@ enabled: true, formatter: function () { return this.point.name; - }, + } // softConnector: true, - y: 5 + //y: 0 }, + ignoreHiddenPoint: true, //innerSize: 0, legendType: 'point', marker: null, // point options are specified in the base options - size: '75%', + size: null, showInLegend: false, slicedOffset: 10, states: { @@ -13979,6 +15462,10 @@ brightness: 0.1, shadow: false } + }, + stickyTracking: false, + tooltip: { + followPointer: true } }); @@ -13996,6 +15483,11 @@ var point = this, toggleSlice; + // Disallow negative values (#1530) + if (point.y < 0) { + point.y = null; + } + //visible: options.visible !== false, extend(point, { visible: point.visible !== false, @@ -14019,34 +15511,32 @@ */ setVisible: function (vis) { var point = this, - chart = point.series.chart, - tracker = point.tracker, - dataLabel = point.dataLabel, - connector = point.connector, - shadowGroup = point.shadowGroup, + series = point.series, + chart = series.chart, method; // if called without an argument, toggle visibility - point.visible = vis = vis === UNDEFINED ? !point.visible : vis; - + point.visible = point.options.visible = vis = vis === UNDEFINED ? !point.visible : vis; + series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data + method = vis ? 'show' : 'hide'; - point.group[method](); - if (tracker) { - tracker[method](); - } - if (dataLabel) { - dataLabel[method](); - } - if (connector) { - connector[method](); - } - if (shadowGroup) { - shadowGroup[method](); - } + // Show and hide associated elements + each(['graphic', 'dataLabel', 'connector', 'shadowGroup'], function (key) { + if (point[key]) { + point[key][method](); + } + }); + if (point.legendItem) { chart.legend.colorizeItem(point, vis); } + + // Handle ignore hidden slices + if (!series.isDirty && series.options.ignoreHiddenPoint) { + series.isDirty = true; + chart.redraw(); + } }, /** @@ -14058,7 +15548,6 @@ var point = this, series = point.series, chart = series.chart, - slicedTranslation = point.slicedTranslation, translation; setAnimation(animation, chart); @@ -14067,13 +15556,16 @@ redraw = pick(redraw, true); // if called without an argument, toggle - sliced = point.sliced = defined(sliced) ? sliced : !point.sliced; + point.sliced = point.options.sliced = sliced = defined(sliced) ? sliced : !point.sliced; + series.options.data[inArray(point, series.data)] = point.options; // update userOptions.data - translation = { - translateX: (sliced ? slicedTranslation[0] : chart.plotLeft), - translateY: (sliced ? slicedTranslation[1] : chart.plotTop) + translation = sliced ? point.slicedTranslation : { + translateX: 0, + translateY: 0 }; - point.group.animate(translation); + + point.graphic.animate(translation); + if (point.shadowGroup) { point.shadowGroup.animate(translation); } @@ -14088,6 +15580,9 @@ type: 'pie', isCartesian: false, pointClass: PiePoint, + requireSorting: false, + noSharedTooltip: true, + trackerGroups: ['group', 'dataLabelsGroup'], pointAttrToOptions: { // mapping between SVG attributes and the corresponding options stroke: 'borderColor', 'stroke-width': 'borderWidth', @@ -14097,43 +15592,41 @@ /** * Pies have one color each point */ - getColor: function () { - // record first color for use in setData - this.initialColor = this.chart.counters.color; - }, + getColor: noop, /** * Animate the pies in */ - animate: function () { + animate: function (init) { var series = this, - points = series.points; - - each(points, function (point) { - var graphic = point.graphic, - args = point.shapeArgs, - up = -mathPI / 2; + points = series.points, + startAngleRad = series.startAngleRad; - if (graphic) { - // start values - graphic.attr({ - r: 0, - start: up, - end: up - }); + if (!init) { + each(points, function (point) { + var graphic = point.graphic, + args = point.shapeArgs; - // animate - graphic.animate({ - r: args.r, - start: args.start, - end: args.end - }, series.options.animation); - } - }); + if (graphic) { + // start values + graphic.attr({ + r: series.center[3] / 2, // animate from inner radius (#779) + start: startAngleRad, + end: startAngleRad + }); - // delete this function to allow it only once - series.animate = null; + // animate + graphic.animate({ + r: args.r, + start: args.start, + end: args.end + }, series.options.animation); + } + }); + // delete this function to allow it only once + series.animate = null; + } }, /** @@ -14157,53 +15650,63 @@ var options = this.options, chart = this.chart, - plotWidth = chart.plotWidth, - plotHeight = chart.plotHeight, - positions = options.center.concat([options.size, options.innerSize || 0]), + slicingRoom = 2 * (options.slicedOffset || 0), + handleSlicingRoom, + plotWidth = chart.plotWidth - 2 * slicingRoom, + plotHeight = chart.plotHeight - 2 * slicingRoom, + centerOption = options.center, + positions = [pick(centerOption[0], '50%'), pick(centerOption[1], '50%'), options.size || '100%', options.innerSize || 0], smallestSize = mathMin(plotWidth, plotHeight), - isPercent; + isPercent; return map(positions, function (length, i) { - isPercent = /%$/.test(length); - return isPercent ? + handleSlicingRoom = i < 2 || (i === 2 && isPercent); + return (isPercent ? // i == 0: centerX, relative to width // i == 1: centerY, relative to height // i == 2: size, relative to smallestSize // i == 4: innerSize, relative to smallestSize [plotWidth, plotHeight, smallestSize, smallestSize][i] * pInt(length) / 100 : - length; + length) + (handleSlicingRoom ? slicingRoom : 0); }); }, /** * Do translation for pie slices */ - translate: function () { + translate: function (positions) { this.generatePoints(); var total = 0, series = this, - cumulative = -0.25, // start at top + cumulative = 0, precision = 1000, // issue #172 options = series.options, slicedOffset = options.slicedOffset, connectorOffset = slicedOffset + options.borderWidth, - positions, - chart = series.chart, start, end, angle, + startAngleRad = series.startAngleRad = mathPI / 180 * ((options.startAngle || 0) % 360 - 90), points = series.points, circ = 2 * mathPI, fraction, radiusX, // the x component of the radius vector for a given point radiusY, - labelDistance = options.dataLabels.distance; + labelDistance = options.dataLabels.distance, + ignoreHiddenPoint = options.ignoreHiddenPoint, + i, + len = points.length, + point; - // get positions - either an integer or a percentage string must be given - series.center = positions = series.getCenter(); + // Get positions - either an integer or a percentage string must be given. + // If positions are passed as a parameter, we're in a recursive loop for adjusting + // space for data labels. + if (!positions) { + series.center = positions = series.getCenter(); + } // utility for getting the x value from a given y, used for anticollision logic in data labels series.getX = function (y, left) { @@ -14216,16 +15719,23 @@ }; // get the total sum - each(points, function (point) { - total += point.y; - }); + for (i = 0; i < len; i++) { + point = points[i]; + total += (ignoreHiddenPoint && !point.visible) ? 0 : point.y; + } - each(points, function (point) { + // Calculate the geometry for each point + for (i = 0; i < len; i++) { + + point = points[i]; + // set start and end angle fraction = total ? point.y / total : 0; - start = mathRound(cumulative * circ * precision) / precision; - cumulative += fraction; - end = mathRound(cumulative * circ * precision) / precision; + start = mathRound((startAngleRad + (cumulative * circ)) * precision) / precision; + if (!ignoreHiddenPoint || point.visible) { + cumulative += fraction; + } + end = mathRound((startAngleRad + (cumulative * circ)) * precision) / precision; // set the shape point.shapeType = 'arc'; @@ -14240,10 +15750,13 @@ // center for the sliced out slice angle = (end + start) / 2; - point.slicedTranslation = map([ - mathCos(angle) * slicedOffset + chart.plotLeft, - mathSin(angle) * slicedOffset + chart.plotTop - ], mathRound); + if (angle > 0.75 * circ) { + angle -= 2 * mathPI; + } + point.slicedTranslation = { + translateX: mathRound(mathCos(angle) * slicedOffset), + translateY: mathRound(mathSin(angle) * slicedOffset) + }; // set the anchor point for tooltips radiusX = mathCos(angle) * positions[2] / 2; @@ -14252,8 +15765,12 @@ positions[0] + radiusX * 0.7, positions[1] + radiusY * 0.7 ]; + + point.half = angle < circ / 4 ? 0 : 1; + point.angle = angle; // set the anchor point for data labels + connectorOffset = mathMin(connectorOffset, labelDistance / 2); // #1678 point.labelPos = [ positions[0] + radiusX + mathCos(angle) * labelDistance, // first break of connector positions[1] + radiusY + mathSin(angle) * labelDistance, // a/a @@ -14263,7 +15780,7 @@ positions[1] + radiusY, // a/a labelDistance < 0 ? // alignment 'center' : - angle < circ / 4 ? 'left' : 'right', // alignment + point.half ? 'right' : 'left', // alignment angle // center angle ]; @@ -14271,38 +15788,14 @@ point.percentage = fraction * 100; point.total = total; - }); - - - this.setTooltipPoints(); - }, - - /** - * Render the slices - */ - render: function () { - var series = this; - - // cache attributes for shapes - series.getAttribs(); - - this.drawPoints(); - - // draw the mouse tracking area - if (series.options.enableMouseTracking !== false) { - series.drawTracker(); - } - - this.drawDataLabels(); - - if (series.options.animation && series.animate) { - series.animate(); } - // (See #322) series.isDirty = series.isDirtyData = false; // means data is in accordance with what you see - series.isDirty = false; // means data is in accordance with what you see + + this.setTooltipPoints(); }, + drawGraph: null, + /** * Draw the data points */ @@ -14313,52 +15806,52 @@ groupTranslation, //center, graphic, - group, + //group, shadow = series.options.shadow, shadowGroup, shapeArgs; + if (shadow && !series.shadowGroup) { + series.shadowGroup = renderer.g('shadow') + .add(series.group); + } + // draw the slices each(series.points, function (point) { graphic = point.graphic; shapeArgs = point.shapeArgs; - group = point.group; shadowGroup = point.shadowGroup; // put the shadow behind all points if (shadow && !shadowGroup) { shadowGroup = point.shadowGroup = renderer.g('shadow') - .attr({ zIndex: 4 }) - .add(); - } - - // create the group the first time - if (!group) { - group = point.group = renderer.g('point') - .attr({ zIndex: 5 }) - .add(); + .add(series.shadowGroup); } // if the point is sliced, use special translation, else use plot area traslation - groupTranslation = point.sliced ? point.slicedTranslation : [chart.plotLeft, chart.plotTop]; - group.translate(groupTranslation[0], groupTranslation[1]); + groupTranslation = point.sliced ? point.slicedTranslation : { + translateX: 0, + translateY: 0 + }; + + //group.translate(groupTranslation[0], groupTranslation[1]); if (shadowGroup) { - shadowGroup.translate(groupTranslation[0], groupTranslation[1]); + shadowGroup.attr(groupTranslation); } // draw the slice if (graphic) { - graphic.animate(shapeArgs); + graphic.animate(extend(shapeArgs, groupTranslation)); } else { point.graphic = graphic = renderer.arc(shapeArgs) .setRadialReference(series.center) - .attr(extend( - point.pointAttr[NORMAL_STATE], - { 'stroke-linejoin': 'round' } - )) - .add(point.group) - .shadow(shadow, shadowGroup); - + .attr( + point.pointAttr[point.selected ? SELECT_STATE : NORMAL_STATE] + ) + .attr({ 'stroke-linejoin': 'round' }) + .attr(groupTranslation) + .add(series.group) + .shadow(shadow, shadowGroup); } // detect point specific visibility @@ -14381,6 +15874,8 @@ options = series.options.dataLabels, connectorPadding = pick(options.connectorPadding, 10), connectorWidth = pick(options.connectorWidth, 1), + plotWidth = chart.plotWidth, + plotHeight = chart.plotHeight, connector, connectorPath, softConnector = pick(options.softConnector, true), @@ -14390,6 +15885,7 @@ centerY = seriesCenter[1], outside = distanceOption > 0, dataLabel, + dataLabelWidth, labelPos, labelHeight, halves = [// divide the points into right and left halves for anti collision @@ -14400,12 +15896,20 @@ y, visibility, rankArr, - sort, - i = 2, - j; + i, + j, + overflow = [0, 0, 0, 0], // top, right, bottom, left + sort = function (a, b) { + return b.y - a.y; + }, + sortByAngle = function (points, sign) { + points.sort(function (a, b) { + return a.angle !== undefined && (b.angle - a.angle) * sign; + }); + }; // get out if not enabled - if (!options.enabled) { + if (!options.enabled && !series._hasPointLabels) { return; } @@ -14415,24 +15919,21 @@ // arrange points for detection collision each(data, function (point) { if (point.dataLabel) { // it may have been cancelled in the base method (#407) - halves[ - point.labelPos[7] < mathPI / 2 ? 0 : 1 - ].push(point); + halves[point.half].push(point); } }); - halves[1].reverse(); - - // define the sorting algorithm - sort = function (a, b) { - return b.y - a.y; - }; // assume equal label heights - labelHeight = halves[0][0] && halves[0][0].dataLabel && (halves[0][0].dataLabel.getBBox().height || 21); // 21 is for #968 + i = 0; + while (!labelHeight && data[i]) { // #1569 + labelHeight = data[i] && data[i].dataLabel && (data[i].dataLabel.getBBox().height || 21); // 21 is for #968 + i++; + } /* Loop over the points in each half, starting from the top and bottom * of the pie to detect overlapping labels. */ + i = 2; while (i--) { var slots = [], @@ -14442,6 +15943,9 @@ pos, length = points.length, slotIndex; + + // Sort by angle + sortByAngle(points, i - 0.5); // Only do anti-collision when we are outside the pie and have connectors (#856) if (distanceOption > 0) { @@ -14449,12 +15953,13 @@ // build the slots for (pos = centerY - radius - distanceOption; pos <= centerY + radius + distanceOption; pos += labelHeight) { slots.push(pos); + // visualize the slot /* var slotX = series.getX(pos, i) + chart.plotLeft - (i ? 100 : 0), slotY = pos + chart.plotTop; if (!isNaN(slotX)) { - chart.renderer.rect(slotX, slotY - 7, 100, labelHeight) + chart.renderer.rect(slotX, slotY - 7, 100, labelHeight, 1) .attr({ 'stroke-width': 1, stroke: 'silver' @@ -14465,7 +15970,7 @@ fill: 'silver' }).add(); } - // */ + */ } slotsLength = slots.length; @@ -14564,58 +16069,183 @@ seriesCenter[0] + (i ? -1 : 1) * (radius + distanceOption) : series.getX(slotIndex === 0 || slotIndex === slots.length - 1 ? naturalY : y, i); - // move or place the data label - dataLabel - .attr({ - visibility: visibility, - align: labelPos[6] - })[dataLabel.moved ? 'animate' : 'attr']({ - x: x + options.x + - ({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0), - y: y + options.y - }); - dataLabel.moved = true; - - // draw the connector - if (outside && connectorWidth) { + + // Record the placement and visibility + dataLabel._attr = { + visibility: visibility, + align: labelPos[6] + }; + dataLabel._pos = { + x: x + options.x + + ({ left: connectorPadding, right: -connectorPadding }[labelPos[6]] || 0), + y: y + options.y - 10 // 10 is for the baseline (label vs text) + }; + dataLabel.connX = x; + dataLabel.connY = y; + + + // Detect overflowing data labels + if (this.options.size === null) { + dataLabelWidth = dataLabel.width; + // Overflow left + if (x - dataLabelWidth < connectorPadding) { + overflow[3] = mathMax(mathRound(dataLabelWidth - x + connectorPadding), overflow[3]); + + // Overflow right + } else if (x + dataLabelWidth > plotWidth - connectorPadding) { + overflow[1] = mathMax(mathRound(x + dataLabelWidth - plotWidth + connectorPadding), overflow[1]); + } + + // Overflow top + if (y - labelHeight / 2 < 0) { + overflow[0] = mathMax(mathRound(-y + labelHeight / 2), overflow[0]); + + // Overflow left + } else if (y + labelHeight / 2 > plotHeight) { + overflow[2] = mathMax(mathRound(y + labelHeight / 2 - plotHeight), overflow[2]); + } + } + } // for each point + } // for each half + + // Do not apply the final placement and draw the connectors until we have verified + // that labels are not spilling over. + if (arrayMax(overflow) === 0 || this.verifyDataLabelOverflow(overflow)) { + + // Place the labels in the final position + this.placeDataLabels(); + + // Draw the connectors + if (outside && connectorWidth) { + each(this.points, function (point) { connector = point.connector; - - connectorPath = softConnector ? [ - M, - x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label - 'C', - x, y, // first break, next to the label - 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5], - labelPos[2], labelPos[3], // second break - L, - labelPos[4], labelPos[5] // base - ] : [ - M, - x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label - L, - labelPos[2], labelPos[3], // second break - L, - labelPos[4], labelPos[5] // base - ]; - - if (connector) { - connector.animate({ d: connectorPath }); - connector.attr('visibility', visibility); - - } else { - point.connector = connector = series.chart.renderer.path(connectorPath).attr({ - 'stroke-width': connectorWidth, - stroke: options.connectorColor || point.color || '#606060', - visibility: visibility, - zIndex: 3 - }) - .translate(chart.plotLeft, chart.plotTop) - .add(); + labelPos = point.labelPos; + dataLabel = point.dataLabel; + + if (dataLabel && dataLabel._pos) { + visibility = dataLabel._attr.visibility; + x = dataLabel.connX; + y = dataLabel.connY; + connectorPath = softConnector ? [ + M, + x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label + 'C', + x, y, // first break, next to the label + 2 * labelPos[2] - labelPos[4], 2 * labelPos[3] - labelPos[5], + labelPos[2], labelPos[3], // second break + L, + labelPos[4], labelPos[5] // base + ] : [ + M, + x + (labelPos[6] === 'left' ? 5 : -5), y, // end of the string at the label + L, + labelPos[2], labelPos[3], // second break + L, + labelPos[4], labelPos[5] // base + ]; + + if (connector) { + connector.animate({ d: connectorPath }); + connector.attr('visibility', visibility); + + } else { + point.connector = connector = series.chart.renderer.path(connectorPath).attr({ + 'stroke-width': connectorWidth, + stroke: options.connectorColor || point.color || '#606060', + visibility: visibility + }) + .add(series.group); + } + } else if (connector) { + point.connector = connector.destroy(); } + }); + } + } + }, + + /** + * Verify whether the data labels are allowed to draw, or we should run more translation and data + * label positioning to keep them inside the plot area. Returns true when data labels are ready + * to draw. + */ + verifyDataLabelOverflow: function (overflow) { + + var center = this.center, + options = this.options, + centerOption = options.center, + minSize = options.minSize || 80, + newSize = minSize, + ret; + + // Handle horizontal size and center + if (centerOption[0] !== null) { // Fixed center + newSize = mathMax(center[2] - mathMax(overflow[1], overflow[3]), minSize); + + } else { // Auto center + newSize = mathMax( + center[2] - overflow[1] - overflow[3], // horizontal overflow + minSize + ); + center[0] += (overflow[3] - overflow[1]) / 2; // horizontal center + } + + // Handle vertical size and center + if (centerOption[1] !== null) { // Fixed center + newSize = mathMax(mathMin(newSize, center[2] - mathMax(overflow[0], overflow[2])), minSize); + + } else { // Auto center + newSize = mathMax( + mathMin( + newSize, + center[2] - overflow[0] - overflow[2] // vertical overflow + ), + minSize + ); + center[1] += (overflow[0] - overflow[2]) / 2; // vertical center + } + + // If the size must be decreased, we need to run translate and drawDataLabels again + if (newSize < center[2]) { + center[2] = newSize; + this.translate(center); + each(this.points, function (point) { + if (point.dataLabel) { + point.dataLabel._pos = null; // reset } - } + }); + this.drawDataLabels(); + + // Else, return true to indicate that the pie and its labels is within the plot area + } else { + ret = true; } + return ret; + }, + + /** + * Perform the final placement of the data labels after we have verified that they + * fall within the plot area. + */ + placeDataLabels: function () { + each(this.points, function (point) { + var dataLabel = point.dataLabel, + _pos; + + if (dataLabel) { + _pos = dataLabel._pos; + if (_pos) { + dataLabel.attr(dataLabel._attr); + dataLabel[dataLabel.moved ? 'animate' : 'attr'](_pos); + dataLabel.moved = true; + } else if (dataLabel) { + dataLabel.attr({ y: -999 }); + } + } + }); }, + + alignDataLabel: noop, /** * Draw point specific tracker objects. Inherit directly from column series. @@ -14630,7 +16260,7 @@ /** * Pies don't have point marker symbols */ - getSymbol: function () {} + getSymbol: noop }; PieSeries = extendClass(Series, PieSeries); @@ -14649,51 +16279,82 @@ NUMBER = 'number', commonOptions = { - approximation: 'average', // average, open, high, low, close, sum - //forced: undefined, - groupPixelWidth: 2, - // the first one is the point or start value, the second is the start value if we're dealing with range, - // the third one is the end value if dealing with a range - dateTimeLabelFormats: hash( - MILLISECOND, ['%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'], - SECOND, ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'], - MINUTE, ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'], - HOUR, ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'], - DAY, ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'], - WEEK, ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'], - MONTH, ['%B %Y', '%B', '-%B %Y'], - YEAR, ['%Y', '%Y', '-%Y'] - ) - // smoothed = false, // enable this for navigator series only + approximation: 'average', // average, open, high, low, close, sum + //enabled: null, // (true for stock charts, false for basic), + //forced: undefined, + groupPixelWidth: 2, + // the first one is the point or start value, the second is the start value if we're dealing with range, + // the third one is the end value if dealing with a range + dateTimeLabelFormats: hash( + MILLISECOND, ['%A, %b %e, %H:%M:%S.%L', '%A, %b %e, %H:%M:%S.%L', '-%H:%M:%S.%L'], + SECOND, ['%A, %b %e, %H:%M:%S', '%A, %b %e, %H:%M:%S', '-%H:%M:%S'], + MINUTE, ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'], + HOUR, ['%A, %b %e, %H:%M', '%A, %b %e, %H:%M', '-%H:%M'], + DAY, ['%A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'], + WEEK, ['Week from %A, %b %e, %Y', '%A, %b %e', '-%A, %b %e, %Y'], + MONTH, ['%B %Y', '%B', '-%B %Y'], + YEAR, ['%Y', '%Y', '-%Y'] + ) + // smoothed = false, // enable this for navigator series only + }, + + specificOptions = { // extends common options + line: {}, + spline: {}, + area: {}, + areaspline: {}, + column: { + approximation: 'sum', + groupPixelWidth: 10 }, - - // units are defined in a separate array to allow complete overriding in case of a user option - defaultDataGroupingUnits = [[ - MILLISECOND, // unit name - [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples - ], [ - SECOND, - [1, 2, 5, 10, 15, 30] - ], [ - MINUTE, - [1, 2, 5, 10, 15, 30] - ], [ - HOUR, - [1, 2, 3, 4, 6, 8, 12] - ], [ - DAY, - [1] - ], [ - WEEK, - [1] - ], [ - MONTH, - [1, 3, 6] - ], [ - YEAR, - null - ] - ], + arearange: { + approximation: 'range' + }, + areasplinerange: { + approximation: 'range' + }, + columnrange: { + approximation: 'range', + groupPixelWidth: 10 + }, + candlestick: { + approximation: 'ohlc', + groupPixelWidth: 10 + }, + ohlc: { + approximation: 'ohlc', + groupPixelWidth: 5 + } + }, + + // units are defined in a separate array to allow complete overriding in case of a user option + defaultDataGroupingUnits = [[ + MILLISECOND, // unit name + [1, 2, 5, 10, 20, 25, 50, 100, 200, 500] // allowed multiples + ], [ + SECOND, + [1, 2, 5, 10, 15, 30] + ], [ + MINUTE, + [1, 2, 5, 10, 15, 30] + ], [ + HOUR, + [1, 2, 3, 4, 6, 8, 12] + ], [ + DAY, + [1] + ], [ + WEEK, + [1] + ], [ + MONTH, + [1, 3, 6] + ], [ + YEAR, + null + ] + ], + /** * Define the available approximation types. The data grouping approximations takes an array @@ -14745,7 +16406,7 @@ close: function (arr) { return arr.length ? arr[arr.length - 1] : (arr.hasNulls ? null : UNDEFINED); }, - // ohlc is a special case where a multidimensional array is input and an array is output + // ohlc and range are special cases where a multidimensional array is input and an array is output ohlc: function (open, high, low, close) { open = approximations.open(open); high = approximations.high(high); @@ -14756,6 +16417,15 @@ return [open, high, low, close]; } // else, return is undefined + }, + range: function (low, high) { + low = approximations.low(low); + high = approximations.high(high); + + if (typeof low === NUMBER || typeof high === NUMBER) { + return [low, high]; + } + // else, return is undefined } }; @@ -14776,11 +16446,10 @@ pointY, groupedY, handleYData = !!yData, // when grouping the fake extended axis for panning, we don't need to consider y - values1 = [], - values2 = [], - values3 = [], - values4 = [], + values = [[], [], [], []], approximationFn = typeof approximation === 'function' ? approximation : approximations[approximation], + pointArrayMap = series.pointArrayMap, + pointArrayMapLength = pointArrayMap && pointArrayMap.length, i; for (i = 0; i <= dataLength; i++) { @@ -14791,7 +16460,7 @@ // get group x and y pointX = groupPositions.shift(); - groupedY = approximationFn(values1, values2, values3, values4); + groupedY = approximationFn.apply(0, values); // push the grouped data if (groupedY !== UNDEFINED) { @@ -14800,10 +16469,10 @@ } // reset the aggregate arrays - values1 = []; - values2 = []; - values3 = []; - values4 = []; + values[0] = []; + values[1] = []; + values[2] = []; + values[3] = []; // don't loop beyond the last group if (i === dataLength) { @@ -14817,45 +16486,29 @@ } // for each raw data point, push it to an array that contains all values for this specific group - pointY = handleYData ? yData[i] : null; - if (approximation === 'ohlc') { + if (pointArrayMap) { var index = series.cropStart + i, - point = (data && data[index]) || series.pointClass.prototype.applyOptions.apply({}, [dataOptions[index]]), - open = point.open, - high = point.high, - low = point.low, - close = point.close; - - - if (typeof open === NUMBER) { - values1.push(open); - } else if (open === null) { - values1.hasNulls = true; - } - - if (typeof high === NUMBER) { - values2.push(high); - } else if (high === null) { - values2.hasNulls = true; - } - - if (typeof low === NUMBER) { - values3.push(low); - } else if (low === null) { - values3.hasNulls = true; + point = (data && data[index]) || series.pointClass.prototype.applyOptions.apply({ series: series }, [dataOptions[index]]), + j, + val; + + for (j = 0; j < pointArrayMapLength; j++) { + val = point[pointArrayMap[j]]; + if (typeof val === NUMBER) { + values[j].push(val); + } else if (val === null) { + values[j].hasNulls = true; + } } - if (typeof close === NUMBER) { - values4.push(close); - } else if (close === null) { - values4.hasNulls = true; - } } else { + pointY = handleYData ? yData[i] : null; + if (typeof pointY === NUMBER) { - values1.push(pointY); + values[0].push(pointY); } else if (pointY === null) { - values1.hasNulls = true; + values[0].hasNulls = true; } } @@ -14869,9 +16522,10 @@ */ seriesProto.processData = function () { var series = this, + chart = series.chart, options = series.options, dataGroupingOptions = options[DATA_GROUPING], - groupingEnabled = dataGroupingOptions && dataGroupingOptions.enabled, + groupingEnabled = dataGroupingOptions && pick(dataGroupingOptions.enabled, chart.options._stock), hasGroupedData; // run base method @@ -14885,9 +16539,7 @@ series.destroyGroupedData(); } - var i, - chart = series.chart, processedXData = series.processedXData, processedYData = series.processedYData, plotSizeX = chart.plotSizeX, @@ -14993,7 +16645,7 @@ /** * Make the tooltip's header reflect the grouped range */ -seriesProto.tooltipHeaderFormatter = function (key) { +seriesProto.tooltipHeaderFormatter = function (point) { var series = this, options = series.options, tooltipOptions = series.tooltipOptions, @@ -15009,7 +16661,7 @@ ret; // apply only to grouped series - if (xAxis && xAxis.options.type === 'datetime' && dataGroupingOptions) { + if (xAxis && xAxis.options.type === 'datetime' && dataGroupingOptions && isNumber(point.key)) { // set variables currentDataGrouping = series.currentDataGrouping; @@ -15027,7 +16679,7 @@ // if not grouped, and we don't have set the xDateFormat option, get the best fit, // so if the least distance between points is one minute, show it, but if the // least distance is one day, skip hours and minutes etc. - } else if (!xDateFormat) { + } else if (!xDateFormat && dateTimeLabelFormats) { for (n in timeUnits) { if (timeUnits[n] >= xAxis.closestPointRange) { xDateFormat = dateTimeLabelFormats[n][0]; @@ -15037,9 +16689,9 @@ } // now format the key - formattedKey = dateFormat(xDateFormat, key); + formattedKey = dateFormat(xDateFormat, point.key); if (xDateFormatEnd) { - formattedKey += dateFormat(xDateFormatEnd, key + currentDataGrouping.totalRange - 1); + formattedKey += dateFormat(xDateFormatEnd, point.key + currentDataGrouping.totalRange - 1); } // return the replaced format @@ -15047,7 +16699,7 @@ // else, fall back to the regular formatter } else { - ret = baseTooltipHeaderFormatter.apply(series, [key]); + ret = baseTooltipHeaderFormatter.call(series, point); } return ret; @@ -15070,19 +16722,37 @@ }; -// Extend the plot options - -// line types -defaultPlotOptions.line[DATA_GROUPING] = - defaultPlotOptions.spline[DATA_GROUPING] = - defaultPlotOptions.area[DATA_GROUPING] = - defaultPlotOptions.areaspline[DATA_GROUPING] = commonOptions; - -// bar-like types (OHLC and candleticks inherit this as the classes are not yet built) -defaultPlotOptions.column[DATA_GROUPING] = merge(commonOptions, { - approximation: 'sum', - groupPixelWidth: 10 +// Handle default options for data grouping. This must be set at runtime because some series types are +// defined after this. +wrap(seriesProto, 'setOptions', function (proceed, itemOptions) { + + var options = proceed.call(this, itemOptions), + type = this.type, + plotOptions = this.chart.options.plotOptions, + defaultOptions = defaultPlotOptions[type].dataGrouping; + + if (specificOptions[type]) { // #1284 + if (!defaultOptions) { + defaultOptions = merge(commonOptions, specificOptions[type]); + } + + options.dataGrouping = merge( + defaultOptions, + plotOptions.series && plotOptions.series.dataGrouping, // #1228 + plotOptions[type].dataGrouping, // Set by the StockChart constructor + itemOptions.dataGrouping + ); + } + + if (this.chart.options._stock) { + this.requireSorting = true; + } + + return options; }); + + + /* **************************************************************************** * End data grouping module * ******************************************************************************//* **************************************************************************** @@ -15092,17 +16762,12 @@ // 1 - Set default options defaultPlotOptions.ohlc = merge(defaultPlotOptions.column, { lineWidth: 1, - dataGrouping: { - approximation: 'ohlc', - enabled: true, - groupPixelWidth: 5 // allows to be packed tighter than candlesticks - }, tooltip: { pointFormat: '{series.name}
    ' + 'Open: {point.open}
    ' + 'High: {point.high}
    ' + 'Low: {point.low}
    ' + - 'Close: {point.close}
    ' + 'Close: {point.close}
    ' }, states: { hover: { @@ -15110,93 +16775,46 @@ } }, threshold: null + //upColor: undefined }); -// 2- Create the OHLCPoint object -var OHLCPoint = extendClass(Point, { - /** - * Apply the options containing the x and OHLC data and possible some extra properties. - * This is called on point init or from point.update. Extends base Point by adding - * multiple y-like values. - * - * @param {Object} options - */ - applyOptions: function (options) { - var point = this, - series = point.series, - i = 0; - - - // object input for example: - // { x: Date(2010, 0, 1), open: 7.88, high: 7.99, low: 7.02, close: 7.65 } - if (typeof options === 'object' && typeof options.length !== 'number') { - - // copy options directly to point - extend(point, options); - - point.options = options; - } else if (options.length) { // array - // with leading x value - if (options.length === 5) { - if (typeof options[0] === 'string') { - point.name = options[0]; - } else if (typeof options[0] === 'number') { - point.x = options[0]; - } - i++; - } - point.open = options[i++]; - point.high = options[i++]; - point.low = options[i++]; - point.close = options[i++]; - } - - /* - * If no x is set by now, get auto incremented value. All points must have an - * x value, however the y value can be null to create a gap in the series - */ - point.y = point.high; - if (point.x === UNDEFINED && series) { - point.x = series.autoIncrement(); - } - return point; - }, - - /** - * A specific OHLC tooltip formatter - */ - tooltipFormatter: function () { - var point = this, - series = point.series; - - return ['', (point.name || series.name), '
    ', - 'Open: ', point.open, '
    ', - 'High: ', point.high, '
    ', - 'Low: ', point.low, '
    ', - 'Close: ', point.close, '
    '].join(''); - - }, - - /** - * Return a plain array for speedy calculation - */ - toYData: function () { - return [this.open, this.high, this.low, this.close]; - } - -}); - -// 3 - Create the OHLCSeries object +// 2 - Create the OHLCSeries object var OHLCSeries = extendClass(seriesTypes.column, { type: 'ohlc', - valueCount: 4, // four values per point - pointClass: OHLCPoint, + pointArrayMap: ['open', 'high', 'low', 'close'], // array point configs are mapped to this + toYData: function (point) { // return a plain array for speedy calculation + return [point.open, point.high, point.low, point.close]; + }, + pointValKey: 'high', pointAttrToOptions: { // mapping between SVG attributes and the corresponding options stroke: 'color', 'stroke-width': 'lineWidth' }, + upColorProp: 'stroke', + + /** + * Postprocess mapping between options and SVG attributes + */ + getAttribs: function () { + seriesTypes.column.prototype.getAttribs.apply(this, arguments); + var series = this, + options = series.options, + stateOptions = options.states, + upColor = options.upColor || series.color, + seriesDownPointAttr = merge(series.pointAttr), + upColorProp = series.upColorProp; + + seriesDownPointAttr[''][upColorProp] = upColor; + seriesDownPointAttr.hover[upColorProp] = stateOptions.hover.upColor || upColor; + seriesDownPointAttr.select[upColorProp] = stateOptions.select.upColor || upColor; + each(series.points, function (point) { + if (point.open < point.close) { + point.pointAttr = seriesDownPointAttr; + } + }); + }, /** * Translate data points from raw values x and y to plotX and plotY @@ -15246,7 +16864,7 @@ // crisp vector coordinates crispCorr = (pointAttr['stroke-width'] % 2) / 2; crispX = mathRound(point.plotX) + crispCorr; - halfWidth = mathRound(point.barW / 2); + halfWidth = mathRound(point.shapeArgs.width / 2); // the vertical stem path = [ @@ -15315,10 +16933,6 @@ // 1 - set default options defaultPlotOptions.candlestick = merge(defaultPlotOptions.column, { - dataGrouping: { - approximation: 'ohlc', - enabled: true - }, lineColor: 'black', lineWidth: 1, states: { @@ -15343,28 +16957,7 @@ stroke: 'lineColor', 'stroke-width': 'lineWidth' }, - - /** - * Postprocess mapping between options and SVG attributes - */ - getAttribs: function () { - OHLCSeries.prototype.getAttribs.apply(this, arguments); - var series = this, - options = series.options, - stateOptions = options.states, - upColor = options.upColor, - seriesDownPointAttr = merge(series.pointAttr); - - seriesDownPointAttr[''].fill = upColor; - seriesDownPointAttr.hover.fill = stateOptions.hover.upColor || upColor; - seriesDownPointAttr.select.fill = stateOptions.select.upColor || upColor; - - each(series.points, function (point) { - if (point.open < point.close) { - point.pointAttr = seriesDownPointAttr; - } - }); - }, + upColorProp: 'fill', /** * Draw the data points @@ -15399,7 +16992,7 @@ plotClose = mathRound(point.plotClose) + crispCorr; topBox = math.min(plotOpen, plotClose); bottomBox = math.max(plotOpen, plotClose); - halfWidth = mathRound(point.barW / 2); + halfWidth = mathRound(point.shapeArgs.width / 2); // create the path path = [ @@ -15471,6 +17064,9 @@ fontWeight: 'bold', textAlign: 'center' }, + tooltip: { + pointFormat: '{point.text}
    ' + }, threshold: null, y: -30 }); @@ -15480,6 +17076,7 @@ type: 'flags', sorted: false, noSharedTooltip: true, + takeOrdinalPosition: false, // #1074 forceCrop: true, /** * Inherit the initialization from base Series @@ -15515,7 +17112,8 @@ step = onSeries && onSeries.options.step, onData = onSeries && onSeries.points, i = onData && onData.length, - xAxisExt = series.xAxis.getExtremes(), + xAxis = series.xAxis, + xAxisExt = xAxis.getExtremes(), leftPoint, lastX, rightPoint; @@ -15567,7 +17165,7 @@ // an undefined plotY, but then we must remove the shapeArgs (#847). if (point.plotY === UNDEFINED) { if (point.x >= xAxisExt.min && point.x <= xAxisExt.max) { // we're inside xAxis range - point.plotY = chart.plotHeight; + point.plotY = xAxis.lineTop - chart.plotTop; } else { point.shapeArgs = {}; // 847 } @@ -15599,13 +17197,12 @@ plotY, options = series.options, optionsY = options.y, - shape = options.shape, + shape, box, bBox, i, point, graphic, - tracker, stackIndex, crisp = (options.lineWidth % 2 / 2), anchorX, @@ -15616,6 +17213,7 @@ point = points[i]; plotX = point.plotX + crisp; stackIndex = point.stackIndex; + shape = point.options.shape || options.shape; plotY = point.plotY; if (plotY !== UNDEFINED) { plotY = point.plotY + optionsY + crisp - (stackIndex !== UNDEFINED && stackIndex * options.stackDistance); @@ -15624,7 +17222,6 @@ anchorY = stackIndex ? UNDEFINED : point.plotY; graphic = point.graphic; - tracker = point.tracker; // only draw the point if y is defined if (plotY !== UNDEFINED) { @@ -15645,7 +17242,8 @@ plotY, shape, anchorX, - anchorY + anchorY, + options.useHTML ) .css(merge(options.style, point.style)) .attr(pointAttr) @@ -15674,9 +17272,6 @@ } else if (graphic) { point.graphic = graphic.destroy(); - if (tracker) { - tracker.attr('y', -9999); - } } } @@ -15687,31 +17282,26 @@ * Extend the column trackers with listeners to expand and contract stacks */ drawTracker: function () { - var series = this; - - seriesTypes.column.prototype.drawTracker.apply(series); + + seriesTypes.column.prototype.drawTracker.apply(this); // put each point in front on mouse over, this allows readability of vertically // stacked elements as well as tight points on the x axis - each(series.points, function (point) { - addEvent(point.tracker.element, 'mouseover', function () { - point.graphic.toFront(); + if (hasSVG) { // Known issue: VML browsers don't bubble up + each(this.points, function (point) { + if (point.graphic) { + addEvent(point.graphic.element, 'mouseover', function () { + point.graphic.toFront(); + }); + } }); - }); - }, - - /** - * Override the regular tooltip formatter by returning the point text given - * in the options - */ - tooltipFormatter: function (item) { - return item.point.text; + } }, /** * Disable animation */ - animate: function () {} + animate: noop }); @@ -15738,10 +17328,14 @@ var anchorX = options && options.anchorX, anchorY = options && options.anchorY, - path = symbols[shape](x, y, w, h); + path = symbols[shape](x, y, w, h), + labelTopOrBottomY; if (anchorX && anchorY) { - path.push('M', anchorX, y + h, 'L', anchorX, anchorY); + // if the label is below the anchor, draw the connecting line from the top edge of the label + // otherwise start drawing from the bottom edge + labelTopOrBottomY = (y > anchorY) ? y : y + h; + path.push('M', anchorX, labelTopOrBottomY, 'L', anchorX, anchorY); } return path; @@ -15760,11 +17354,6 @@ /* **************************************************************************** * End Flags series code * *****************************************************************************/ -// constants -var MOUSEDOWN = hasTouch ? 'touchstart' : 'mousedown', - MOUSEMOVE = hasTouch ? 'touchmove' : 'mousemove', - MOUSEUP = hasTouch ? 'touchend' : 'mouseup'; - /* **************************************************************************** * Start Scroller code * *****************************************************************************/ @@ -15806,7 +17395,8 @@ units: units }, dataLabels: { - enabled: false + enabled: false, + zIndex: 2 // #1839 }, id: PREFIX + 'navigator-series', lineColor: '#4572A7', @@ -15846,7 +17436,7 @@ }, scrollbar: { //enabled: true - height: hasTouch ? 20 : 14, + height: isTouchDevice ? 20 : 14, barBackgroundColor: buttonGradient, barBorderRadius: 2, barBorderWidth: 1, @@ -15856,6 +17446,7 @@ buttonBorderColor: '#666', buttonBorderRadius: 2, buttonBorderWidth: 1, + minWidth: 6, rifleColor: '#666', trackBackgroundColor: hash( LINEAR_GRADIENT, { x1: 0, y1: 0, x2: 0, y2: 1 }, @@ -15865,8 +17456,9 @@ ] ), trackBorderColor: '#CCC', - trackBorderWidth: 1 + trackBorderWidth: 1, // trackBorderRadius: 0 + liveRedraw: hasSVG } }); /*jslint white:false */ @@ -15892,9 +17484,7 @@ this.handles = []; this.scrollbarButtons = []; this.elementsToDestroy = []; // Array containing the elements to destroy when Scroller is destroyed - - chart.resetZoomEnabled = false; - + this.chart = chart; this.height = height; this.scrollbarHeight = scrollbarHeight; @@ -15910,13 +17500,6 @@ Scroller.prototype = { /** - * Return the top of the navigation - */ - getAxisTop: function (chartHeight) { - return this.navigatorOptions.top || chartHeight - this.height - this.scrollbarHeight - this.chart.options.chart.spacingBottom; - }, - - /** * Draw one of the handles on the side of the zoomed range in the navigator * @param {Number} x The x center for the handle * @param {Number} index 0 for left and 1 for right @@ -15964,7 +17547,11 @@ elementsToDestroy.push(tempElem); } - handles[index].translate(scroller.scrollerLeft + scroller.scrollbarHeight + parseInt(x, 10), scroller.top + scroller.height / 2 - 8); + // Place it + handles[index][chart.isResizing ? 'animate' : 'attr']({ + translateX: scroller.scrollerLeft + scroller.scrollbarHeight + parseInt(x, 10), + translateY: scroller.top + scroller.height / 2 - 8 + }); }, /** @@ -16034,6 +17621,7 @@ scrollerLeft, scrollerWidth, scrollbarGroup = scroller.scrollbarGroup, + navigatorGroup = scroller.navigatorGroup, scrollbar = scroller.scrollbar, xAxis = scroller.xAxis, scrollbarTrack = scroller.scrollbarTrack, @@ -16041,6 +17629,7 @@ scrollbarEnabled = scroller.scrollbarEnabled, navigatorOptions = scroller.navigatorOptions, scrollbarOptions = scroller.scrollbarOptions, + scrollbarMinWidth = scrollbarOptions.minWidth, height = scroller.height, top = scroller.top, navigatorEnabled = scroller.navigatorEnabled, @@ -16049,12 +17638,16 @@ zoomedMin, zoomedMax, range, + scrX, + scrWidth, + scrollbarPad = 0, outlineHeight = scroller.outlineHeight, barBorderRadius = scrollbarOptions.barBorderRadius, strokeWidth, scrollbarStrokeWidth = scrollbarOptions.barBorderWidth, centerBarX, - outlineTop = top + halfOutline; + outlineTop = top + halfOutline, + verb; // don't render the navigator until we have data (#486) if (isNaN(min)) { @@ -16088,9 +17681,11 @@ pxMin = pick(pxMin, xAxis.translate(min)); pxMax = pick(pxMax, xAxis.translate(max)); - // handles are allowed to cross - scroller.zoomedMin = zoomedMin = pInt(mathMin(pxMin, pxMax)); - scroller.zoomedMax = zoomedMax = pInt(mathMax(pxMin, pxMax)); + // handles are allowed to cross, but never exceed the plot area + scroller.zoomedMax = zoomedMax = mathMin(pInt(mathMax(pxMin, pxMax)), navigatorWidth); + scroller.zoomedMin = zoomedMin = scroller.fixedWidth ? + zoomedMax - scroller.fixedWidth : + mathMax(pInt(mathMin(pxMin, pxMax)), 0); scroller.range = range = zoomedMax - zoomedMin; // on first render, create all elements @@ -16098,29 +17693,33 @@ if (navigatorEnabled) { + // draw the navigator group + scroller.navigatorGroup = navigatorGroup = renderer.g('navigator') + .attr({ + zIndex: 3 + }) + .add(); + scroller.leftShade = renderer.rect() .attr({ - fill: navigatorOptions.maskFill, - zIndex: 3 - }).add(); + fill: navigatorOptions.maskFill + }).add(navigatorGroup); scroller.rightShade = renderer.rect() .attr({ - fill: navigatorOptions.maskFill, - zIndex: 3 - }).add(); + fill: navigatorOptions.maskFill + }).add(navigatorGroup); scroller.outline = renderer.path() .attr({ 'stroke-width': outlineWidth, - stroke: navigatorOptions.outlineColor, - zIndex: 3 + stroke: navigatorOptions.outlineColor }) - .add(); + .add(navigatorGroup); } if (scrollbarEnabled) { // draw the scrollbar group - scroller.scrollbarGroup = scrollbarGroup = renderer.g().add(); + scroller.scrollbarGroup = scrollbarGroup = renderer.g('scrollbar').add(); // the scrollbar track strokeWidth = scrollbarOptions.trackBorderWidth; @@ -16155,20 +17754,21 @@ } // place elements + verb = chart.isResizing ? 'animate' : 'attr'; if (navigatorEnabled) { - scroller.leftShade.attr({ + scroller.leftShade[verb]({ x: navigatorLeft, y: top, width: zoomedMin, height: height }); - scroller.rightShade.attr({ + scroller.rightShade[verb]({ x: navigatorLeft + zoomedMax, y: top, width: navigatorWidth - zoomedMax, height: height }); - scroller.outline.attr({ d: [ + scroller.outline[verb]({ d: [ M, scrollerLeft, outlineTop, // left L, @@ -16192,37 +17792,53 @@ scroller.drawScrollbarButton(0); scroller.drawScrollbarButton(1); - scrollbarGroup.translate(scrollerLeft, mathRound(outlineTop + height)); + scrollbarGroup[verb]({ + translateX: scrollerLeft, + translateY: mathRound(outlineTop + height) + }); - scrollbarTrack.attr({ + scrollbarTrack[verb]({ width: scrollerWidth }); - scrollbar.attr({ - x: mathRound(scrollbarHeight + zoomedMin) + (scrollbarStrokeWidth % 2 / 2), - width: range - scrollbarStrokeWidth + // prevent the scrollbar from drawing to small (#1246) + scrX = scrollbarHeight + zoomedMin; + scrWidth = range - scrollbarStrokeWidth; + if (scrWidth < scrollbarMinWidth) { + scrollbarPad = (scrollbarMinWidth - scrWidth) / 2; + scrWidth = scrollbarMinWidth; + scrX -= scrollbarPad; + } + scroller.scrollbarPad = scrollbarPad; + scrollbar[verb]({ + x: mathFloor(scrX) + (scrollbarStrokeWidth % 2 / 2), + width: scrWidth }); centerBarX = scrollbarHeight + zoomedMin + range / 2 - 0.5; - scroller.scrollbarRifles.attr({ d: [ - M, - centerBarX - 3, scrollbarHeight / 4, - L, - centerBarX - 3, 2 * scrollbarHeight / 3, - M, - centerBarX, scrollbarHeight / 4, - L, - centerBarX, 2 * scrollbarHeight / 3, - M, - centerBarX + 3, scrollbarHeight / 4, - L, - centerBarX + 3, 2 * scrollbarHeight / 3 - ], - visibility: range > 12 ? VISIBLE : HIDDEN - }); + scroller.scrollbarRifles + .attr({ + visibility: range > 12 ? VISIBLE : HIDDEN + })[verb]({ + d: [ + M, + centerBarX - 3, scrollbarHeight / 4, + L, + centerBarX - 3, 2 * scrollbarHeight / 3, + M, + centerBarX, scrollbarHeight / 4, + L, + centerBarX, 2 * scrollbarHeight / 3, + M, + centerBarX + 3, scrollbarHeight / 4, + L, + centerBarX + 3, 2 * scrollbarHeight / 3 + ] + }); } + scroller.scrollbarPad = scrollbarPad; scroller.rendered = true; }, @@ -16230,26 +17846,46 @@ * Set up the mouse and touch events for the navigator and scrollbar */ addEvents: function () { - var scroller = this, - chart = scroller.chart; - - addEvent(chart.container, MOUSEDOWN, scroller.mouseDownHandler); - addEvent(chart.container, MOUSEMOVE, scroller.mouseMoveHandler); - addEvent(document, MOUSEUP, scroller.mouseUpHandler); + var container = this.chart.container, + mouseDownHandler = this.mouseDownHandler, + mouseMoveHandler = this.mouseMoveHandler, + mouseUpHandler = this.mouseUpHandler, + _events; + + // Mouse events + _events = [ + [container, 'mousedown', mouseDownHandler], + [container, 'mousemove', mouseMoveHandler], + [document, 'mouseup', mouseUpHandler] + ]; + + // Touch events + if (hasTouch) { + _events.push( + [container, 'touchstart', mouseDownHandler], + [container, 'touchmove', mouseMoveHandler], + [document, 'touchend', mouseUpHandler] + ); + } + + // Add them all + each(_events, function (args) { + addEvent.apply(null, args); + }); + this._events = _events; }, /** * Removes the event handlers attached previously with addEvents. */ removeEvents: function () { - var scroller = this, - chart = scroller.chart; - - removeEvent(chart.container, MOUSEDOWN, scroller.mouseDownHandler); - removeEvent(chart.container, MOUSEMOVE, scroller.mouseMoveHandler); - removeEvent(document, MOUSEUP, scroller.mouseUpHandler); - if (scroller.navigatorEnabled) { - removeEvent(scroller.baseSeries, 'updatedData', scroller.updatedDataHandler); + + each(this._events, function (args) { + removeEvent.apply(null, args); + }); + this._events = UNDEFINED; + if (this.navigatorEnabled && this.baseSeries) { + removeEvent(this.baseSeries, 'updatedData', this.updatedDataHandler); } }, @@ -16277,7 +17913,7 @@ * Event handler for the mouse down event. */ scroller.mouseDownHandler = function (e) { - e = chart.tracker.normalizeMouseEvent(e); + e = chart.pointer.normalize(e); var zoomedMin = scroller.zoomedMin, zoomedMax = scroller.zoomedMax, @@ -16287,10 +17923,13 @@ scrollerWidth = scroller.scrollerWidth, navigatorLeft = scroller.navigatorLeft, navigatorWidth = scroller.navigatorWidth, + scrollbarPad = scroller.scrollbarPad, range = scroller.range, chartX = e.chartX, chartY = e.chartY, - handleSensitivity = hasTouch ? 10 : 7, + baseXAxis = chart.xAxis[0], + leftValue, + handleSensitivity = isTouchDevice ? 10 : 7, left, isOnNavigator; @@ -16308,12 +17947,19 @@ scroller.otherHandlePos = zoomedMin; // grab the zoomed range - } else if (chartX > navigatorLeft + zoomedMin && chartX < navigatorLeft + zoomedMax) { + } else if (chartX > navigatorLeft + zoomedMin - scrollbarPad && chartX < navigatorLeft + zoomedMax + scrollbarPad) { scroller.grabbedCenter = chartX; - defaultBodyCursor = bodyStyle.cursor; - bodyStyle.cursor = 'ew-resize'; + scroller.fixedWidth = range; + + // In SVG browsers, change the cursor. IE6 & 7 produce an error on changing the cursor, + // and IE8 isn't able to show it while dragging anyway. + if (chart.renderer.isSVG) { + defaultBodyCursor = bodyStyle.cursor; + bodyStyle.cursor = 'ew-resize'; + } dragOffset = chartX - zoomedMin; + // shift the range by clicking on shaded areas, scrollbar track or scrollbar buttons } else if (chartX > scrollerLeft && chartX < scrollerLeft + scrollerWidth) { @@ -16338,14 +17984,21 @@ left = navigatorWidth - range; } if (left !== zoomedMin) { // it has actually moved - chart.xAxis[0].setExtremes( - xAxis.translate(left, true), - xAxis.translate(left + range, true), + scroller.fixedWidth = range; // #1370 + if (!baseXAxis.ordinalPositions) { + baseXAxis.fixedRange = baseXAxis.max - baseXAxis.min; + } + leftValue = xAxis.translate(left, true); + baseXAxis.setExtremes( + leftValue, + baseXAxis.fixedRange ? leftValue + baseXAxis.fixedRange : xAxis.translate(left + range, true), true, - false + false, + { trigger: 'navigator' } ); } } + } }; @@ -16358,58 +18011,76 @@ navigatorWidth = scroller.navigatorWidth, scrollerLeft = scroller.scrollerLeft, scrollerWidth = scroller.scrollerWidth, - range = scroller.range; - - e = chart.tracker.normalizeMouseEvent(e); - var chartX = e.chartX; - - // validation for handle dragging - if (chartX < navigatorLeft) { - chartX = navigatorLeft; - } else if (chartX > scrollerLeft + scrollerWidth - scrollbarHeight) { - chartX = scrollerLeft + scrollerWidth - scrollbarHeight; - } - - // drag left handle - if (scroller.grabbedLeft) { - hasDragged = true; - scroller.render(0, 0, chartX - navigatorLeft, scroller.otherHandlePos); - - // drag right handle - } else if (scroller.grabbedRight) { - hasDragged = true; - scroller.render(0, 0, scroller.otherHandlePos, chartX - navigatorLeft); - - // drag scrollbar or open area in navigator - } else if (scroller.grabbedCenter) { - hasDragged = true; - if (chartX < dragOffset) { // outside left - chartX = dragOffset; - } else if (chartX > navigatorWidth + dragOffset - range) { // outside right - chartX = navigatorWidth + dragOffset - range; + range = scroller.range, + chartX; + + // In iOS, a mousemove event with e.pageX === 0 is fired when holding the finger + // down in the center of the scrollbar. This should be ignored. + if (e.pageX !== 0) { + + e = chart.pointer.normalize(e); + chartX = e.chartX; + + // validation for handle dragging + if (chartX < navigatorLeft) { + chartX = navigatorLeft; + } else if (chartX > scrollerLeft + scrollerWidth - scrollbarHeight) { + chartX = scrollerLeft + scrollerWidth - scrollbarHeight; + } + + // drag left handle + if (scroller.grabbedLeft) { + hasDragged = true; + scroller.render(0, 0, chartX - navigatorLeft, scroller.otherHandlePos); + + // drag right handle + } else if (scroller.grabbedRight) { + hasDragged = true; + scroller.render(0, 0, scroller.otherHandlePos, chartX - navigatorLeft); + + // drag scrollbar or open area in navigator + } else if (scroller.grabbedCenter) { + + hasDragged = true; + if (chartX < dragOffset) { // outside left + chartX = dragOffset; + } else if (chartX > navigatorWidth + dragOffset - range) { // outside right + chartX = navigatorWidth + dragOffset - range; + } + + scroller.render(0, 0, chartX - dragOffset, chartX - dragOffset + range); + } + if (hasDragged && scroller.scrollbarOptions.liveRedraw) { + setTimeout(function () { + scroller.mouseUpHandler(e); + }, 0); } - - scroller.render(0, 0, chartX - dragOffset, chartX - dragOffset + range); } }; /** * Event handler for the mouse up event. */ - scroller.mouseUpHandler = function () { - var zoomedMin = scroller.zoomedMin, - zoomedMax = scroller.zoomedMax; - + scroller.mouseUpHandler = function (e) { + if (hasDragged) { chart.xAxis[0].setExtremes( - xAxis.translate(zoomedMin, true), - xAxis.translate(zoomedMax, true), + xAxis.translate(scroller.zoomedMin, true), + xAxis.translate(scroller.zoomedMax, true), true, - false + false, + { + trigger: 'navigator', + DOMEvent: e // #1838 + } ); } - scroller.grabbedLeft = scroller.grabbedRight = scroller.grabbedCenter = hasDragged = dragOffset = null; - bodyStyle.cursor = defaultBodyCursor; + + if (e.type !== 'mousemove') { + scroller.grabbedLeft = scroller.grabbedRight = scroller.grabbedCenter = scroller.fixedWidth = hasDragged = dragOffset = null; + bodyStyle.cursor = defaultBodyCursor || ''; + } + }; scroller.updatedDataHandler = function () { @@ -16457,7 +18128,8 @@ // update the extremes if (hasSetExtremes && (stickToMin || stickToMax)) { - baseXAxis.setExtremes(newMin, newMax, true, false); + baseXAxis.setExtremes(newMin, newMax, true, false, { trigger: 'updatedData' }); + // if it is not at any edge, just move the scroller window to reflect the new series data } else { if (doRedraw) { @@ -16472,33 +18144,28 @@ }; var xAxisIndex = chart.xAxis.length, - yAxisIndex = chart.yAxis.length, - baseChartSetSize = chart.setSize; + yAxisIndex = chart.yAxis.length; // make room below the chart chart.extraBottomMargin = scroller.outlineHeight + navigatorOptions.margin; - // get the top offset - scroller.top = top = scroller.getAxisTop(chart.chartHeight); - + if (scroller.navigatorEnabled) { - var baseOptions = baseSeries.options, + var baseOptions = baseSeries ? baseSeries.options : {}, mergedNavSeriesOptions, baseData = baseOptions.data, navigatorSeriesOptions = navigatorOptions.series; // remove it to prevent merging one by one navigatorData = navigatorSeriesOptions.data; - baseOptions.data = navigatorSeriesOptions.data = null; // an x axis is required for scrollbar also scroller.xAxis = xAxis = new Axis(chart, merge({ - ordinal: baseSeries.xAxis.options.ordinal // inherit base xAxis' ordinal option + ordinal: baseSeries && baseSeries.xAxis.options.ordinal // inherit base xAxis' ordinal option }, navigatorOptions.xAxis, { isX: true, type: 'datetime', index: xAxisIndex, height: height, - top: top, offset: 0, offsetLeft: scrollbarHeight, offsetRight: -scrollbarHeight, @@ -16512,14 +18179,13 @@ scroller.yAxis = yAxis = new Axis(chart, merge(navigatorOptions.yAxis, { alignTicks: false, height: height, - top: top, offset: 0, index: yAxisIndex, zoomEnabled: false })); // dmerge the series options - mergedNavSeriesOptions = merge(baseSeries.options, navigatorSeriesOptions, { + mergedNavSeriesOptions = merge(baseOptions, navigatorSeriesOptions, { threshold: null, clip: false, enableMouseTracking: false, @@ -16534,16 +18200,19 @@ }); // set the data back - baseOptions.data = baseData; - navigatorSeriesOptions.data = navigatorData; mergedNavSeriesOptions.data = navigatorData || baseData; // add the series navigatorSeries = chart.initSeries(mergedNavSeriesOptions); - // respond to updated data in the base series - // todo: use similiar hook when base series is not yet initialized - addEvent(baseSeries, 'updatedData', scroller.updatedDataHandler); + // Respond to updated data in the base series. + // Abort if lazy-loading data from the server. + if (baseSeries && navigatorOptions.adaptToUpdatedData !== false) { + addEvent(baseSeries, 'updatedData', scroller.updatedDataHandler); + // Survive Series.update() + baseSeries.userOptions.events = extend(baseSeries.userOptions.event, { updatedData: scroller.updatedDataHandler }); + } + // in case of scrollbar only, fake an x axis to get translation } else { @@ -16566,16 +18235,32 @@ // Expose the navigator seris scroller.series = navigatorSeries; - // Override the chart.setSize method to adjust the xAxis and yAxis top option as well. - // This needs to be done prior to chart.resize - chart.setSize = function (width, height, animation) { - scroller.top = top = scroller.getAxisTop(height); + /** + * For stock charts, extend the Chart.getMargins method so that we can set the final top position + * of the navigator once the height of the chart, including the legend, is determined. #367. + */ + wrap(chart, 'getMargins', function (proceed) { + + var legend = this.legend, + legendOptions = legend.options; + + proceed.call(this); + + // Compute the top position + scroller.top = top = scroller.navigatorOptions.top || + this.chartHeight - scroller.height - scroller.scrollbarHeight - this.options.chart.spacingBottom - + (legendOptions.verticalAlign === 'bottom' && legendOptions.enabled && !legendOptions.floating ? + legend.legendHeight + pick(legendOptions.margin, 10) : 0); + if (xAxis && yAxis) { // false if navigator is disabled (#904) + xAxis.options.top = yAxis.options.top = top; + + xAxis.setAxisSize(); + yAxis.setAxisSize(); } - - baseChartSetSize.call(chart, width, height, animation); - }; + }); + scroller.addEvents(); }, @@ -16606,6 +18291,63 @@ Highcharts.Scroller = Scroller; + +/** + * For Stock charts, override selection zooming with some special features because + * X axis zooming is already allowed by the Navigator and Range selector. + */ +wrap(Axis.prototype, 'zoom', function (proceed, newMin, newMax) { + var chart = this.chart, + chartOptions = chart.options, + zoomType = chartOptions.chart.zoomType, + previousZoom, + navigator = chartOptions.navigator, + rangeSelector = chartOptions.rangeSelector, + ret; + + if (this.isXAxis && ((navigator && navigator.enabled) || + (rangeSelector && rangeSelector.enabled))) { + + // For x only zooming, fool the chart.zoom method not to create the zoom button + // because the property already exists + if (zoomType === 'x') { + chart.resetZoomButton = 'blocked'; + + // For y only zooming, ignore the X axis completely + } else if (zoomType === 'y') { + ret = false; + + // For xy zooming, record the state of the zoom before zoom selection, then when + // the reset button is pressed, revert to this state + } else if (zoomType === 'xy') { + previousZoom = this.previousZoom; + if (defined(newMin)) { + this.previousZoom = [this.min, this.max]; + } else if (previousZoom) { + newMin = previousZoom[0]; + newMax = previousZoom[1]; + delete this.previousZoom; + } + } + + } + return ret !== UNDEFINED ? ret : proceed.call(this, newMin, newMax); +}); + +// Initialize scroller for stock charts +wrap(Chart.prototype, 'init', function (proceed, options, callback) { + + addEvent(this, 'beforeRender', function () { + var options = this.options; + if (options.navigator.enabled || options.scrollbar.enabled) { + this.scroller = new Scroller(this); + } + }); + + proceed.call(this, options, callback); + +}); + /* **************************************************************************** * End Scroller code * *****************************************************************************/ @@ -16622,28 +18364,30 @@ height: 16, padding: 1, r: 0, + stroke: '#68A', zIndex: 7 // #484, #852 // states: { // hover: {}, // select: {} // } - } + }, + inputPosition: { + align: 'right' + }, // inputDateFormat: '%b %e, %Y', // inputEditDateFormat: '%Y-%m-%d', // inputEnabled: true, - // inputStyle: {} - // labelStyle: {} + //inputStyle: {}, + labelStyle: { + color: '#666' + } // selected: undefined - // todo: - // - button styles for normal, hover and select state - // - CSS text styles - // - styles for the inputs and labels } }); defaultOptions.lang = merge(defaultOptions.lang, { rangeSelectorZoom: 'Zoom', - rangeSelectorFrom: 'From:', - rangeSelectorTo: 'To:' + rangeSelectorFrom: 'From', + rangeSelectorTo: 'To' }); /** @@ -16651,38 +18395,9 @@ * @param {Object} chart */ function RangeSelector(chart) { - var defaultButtons = [{ - type: 'month', - count: 1, - text: '1m' - }, { - type: 'month', - count: 3, - text: '3m' - }, { - type: 'month', - count: 6, - text: '6m' - }, { - type: 'ytd', - text: 'YTD' - }, { - type: 'year', - count: 1, - text: '1y' - }, { - type: 'all', - text: 'All' - }]; - - chart.resetZoomEnabled = false; - - this.chart = chart; - this.buttons = []; - this.boxSpanElements = {}; // Run RangeSelector - this.init(defaultButtons); + this.init(chart); } RangeSelector.prototype = { @@ -16704,10 +18419,11 @@ navDataMax = navExtremes && navExtremes.dataMax, baseDataMin = extremes && extremes.dataMin, baseDataMax = extremes && extremes.dataMax, - dataMin = mathMin(baseDataMin, pick(navDataMin, baseDataMin)), - dataMax = mathMax(baseDataMax, pick(navDataMax, baseDataMax)), + // if both are defined, get Math.min, else, pick the one that is defined + dataMin = ((defined(baseDataMin) && defined(navDataMin)) ? mathMin : pick)(baseDataMin, navDataMin), + dataMax = ((defined(baseDataMax) && defined(navDataMax)) ? mathMax : pick)(baseDataMax, navDataMax), newMin, - newMax = baseAxis && mathMin(extremes.max, dataMax), + newMax = baseAxis && mathMin(extremes.max, pick(dataMax, extremes.max)), // #1568 now, date = new Date(newMax), type = rangeOptions.type, @@ -16725,7 +18441,8 @@ hour: 3600 * 1000, day: 24 * 3600 * 1000, week: 7 * 24 * 3600 * 1000 - }; + }, + timeName; if (dataMin === null || dataMax === null || // chart has no data, base series is removed i === rangeSelector.selected) { // same button is clicked twice @@ -16735,28 +18452,47 @@ if (fixedTimes[type]) { range = fixedTimes[type] * count; newMin = mathMax(newMax - range, dataMin); - } else if (type === 'month') { - date.setMonth(date.getMonth() - count); - newMin = mathMax(date.getTime(), dataMin); - range = 30 * 24 * 3600 * 1000 * count; + + } else if (type === 'month' || type === 'year') { + timeName = { month: 'Month', year: 'FullYear'}[type]; + date['set' + timeName](date['get' + timeName]() - count); + newMin = mathMax(date.getTime(), pick(dataMin, Number.MIN_VALUE)); // #1568 + range = { month: 30, year: 365 }[type] * 24 * 3600 * 1000 * count; + } else if (type === 'ytd') { - date = new Date(0); - now = new Date(dataMax); - year = now.getFullYear(); - date.setFullYear(year); - - // workaround for IE6 bug, which sets year to next year instead of current - if (String(year) !== dateFormat('%Y', date)) { - date.setFullYear(year - 1); - } - - newMin = rangeMin = mathMax(dataMin || 0, date.getTime()); - now = now.getTime(); - newMax = mathMin(dataMax || now, now); - } else if (type === 'year') { - date.setFullYear(date.getFullYear() - count); - newMin = mathMax(dataMin, date.getTime()); - range = 365 * 24 * 3600 * 1000 * count; + + // On user clicks on the buttons, or a delayed action running from the beforeRender + // event (below), the baseAxis is defined. + if (baseAxis) { + + // When "ytd" is the pre-selected button for the initial view, its calculation + // is delayed and rerun in the beforeRender event (below). When the series + // are initialized, but before the chart is rendered, we have access to the xData + // array (#942). + if (dataMax === UNDEFINED) { + dataMin = Number.MAX_VALUE; + dataMax = Number.MIN_VALUE; + each(chart.series, function (series) { + var xData = series.xData; // reassign it to the last item + dataMin = mathMin(xData[0], dataMin); + dataMax = mathMax(xData[xData.length - 1], dataMax); + }); + redraw = false; + } + now = new Date(dataMax); + year = now.getFullYear(); + newMin = rangeMin = mathMax(dataMin || 0, Date.UTC(year, 0, 1)); + now = now.getTime(); + newMax = mathMin(dataMax || now, now); + + // "ytd" is pre-selected. We don't yet have access to processed point and extremes data + // (things like pointStart and pointInterval are missing), so we delay the process (#942) + } else { + addEvent(chart, 'beforeRender', function () { + rangeSelector.clickButton(i, rangeOptions); + }); + return; + } } else if (type === 'all' && baseAxis) { newMin = dataMin; newMax = dataMax; @@ -16779,48 +18515,75 @@ ); rangeSelector.selected = i; } else { // existing axis object; after render time - setTimeout(function () { // make sure the visual state is set before the heavy process begins - baseAxis.setExtremes( - newMin, - newMax, - pick(redraw, 1), - 0, - { rangeSelectorButton: rangeOptions } - ); - rangeSelector.selected = i; - }, 1); + baseAxis.setExtremes( + newMin, + newMax, + pick(redraw, 1), + 0, + { + trigger: 'rangeSelectorButton', + rangeSelectorButton: rangeOptions + } + ); + rangeSelector.selected = i; } }, + + /** + * The default buttons for pre-selecting time frames + */ + defaultButtons: [{ + type: 'month', + count: 1, + text: '1m' + }, { + type: 'month', + count: 3, + text: '3m' + }, { + type: 'month', + count: 6, + text: '6m' + }, { + type: 'ytd', + text: 'YTD' + }, { + type: 'year', + count: 1, + text: '1y' + }, { + type: 'all', + text: 'All' + }], /** * Initialize the range selector */ - init: function (defaultButtons) { + init: function (chart) { + var rangeSelector = this, - chart = rangeSelector.chart, options = chart.options.rangeSelector, - buttonOptions = options.buttons || defaultButtons, - buttons = rangeSelector.buttons, - leftBox = rangeSelector.leftBox, - rightBox = rangeSelector.rightBox, - selectedOption = options.selected; + buttonOptions = options.buttons || [].concat(rangeSelector.defaultButtons), + buttons = rangeSelector.buttons = [], + selectedOption = options.selected, + blurInputs = rangeSelector.blurInputs = function () { + var minInput = rangeSelector.minInput, + maxInput = rangeSelector.maxInput; + if (minInput) { + minInput.blur(); + } + if (maxInput) { + maxInput.blur(); + } + }; + rangeSelector.chart = chart; + chart.extraTopMargin = 25; rangeSelector.buttonOptions = buttonOptions; - /** - * The handler connected to container that handles mousedown. - */ - rangeSelector.mouseDownHandler = function () { - if (leftBox) { - leftBox.blur(); - } - if (rightBox) { - rightBox.blur(); - } - }; - - addEvent(chart.container, MOUSEDOWN, rangeSelector.mouseDownHandler); + addEvent(chart.container, 'mousedown', blurInputs); + addEvent(chart, 'resize', blurInputs); // zoomed range based on a pre-selected button index if (selectedOption !== UNDEFINED && buttonOptions[selectedOption]) { @@ -16830,31 +18593,31 @@ // normalize the pressed button whenever a new range is selected addEvent(chart, 'load', function () { addEvent(chart.xAxis[0], 'afterSetExtremes', function () { - if (buttons[rangeSelector.selected] && !chart.renderer.forExport) { - buttons[rangeSelector.selected].setState(0); + if (this.fixedRange !== this.max - this.min) { + if (buttons[rangeSelector.selected] && !chart.renderer.forExport) { + buttons[rangeSelector.selected].setState(0); + } + rangeSelector.selected = null; } - rangeSelector.selected = null; + this.fixedRange = null; }); }); }, - - + /** * Set the internal and displayed value of a HTML input for the dates - * @param {Object} input + * @param {String} name * @param {Number} time */ - setInputValue: function (input, time) { - var rangeSelector = this, - chart = rangeSelector.chart, - options = chart.options.rangeSelector, - format = input.hasFocus ? options.inputEditDateFormat || '%Y-%m-%d' : options.inputDateFormat || '%b %e, %Y'; + setInputValue: function (name, time) { + var options = this.chart.options.rangeSelector; if (time) { - input.HCTime = time; + this[name + 'Input'].HCTime = time; } - - input.value = dateFormat(format, input.HCTime); + + this[name + 'Input'].value = dateFormat(options.inputEditDateFormat || '%Y-%m-%d', this[name + 'Input'].HCTime); + this[name + 'DateBox'].attr({ text: dateFormat(options.inputDateFormat || '%b %e, %Y', this[name + 'Input'].HCTime) }); }, /** @@ -16864,37 +18627,82 @@ drawInput: function (name) { var rangeSelector = this, chart = rangeSelector.chart, + chartStyle = chart.options.chart.style, + renderer = chart.renderer, options = chart.options.rangeSelector, - boxSpanElements = rangeSelector.boxSpanElements, lang = defaultOptions.lang, div = rangeSelector.div, isMin = name === 'min', - input; + input, + label, + dateBox, + inputGroup = this.inputGroup; - // create the text label - boxSpanElements[name] = createElement('span', { - innerHTML: lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'] - }, options.labelStyle, div); + // Create the text label + this[name + 'Label'] = label = renderer.label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset) + .attr({ + padding: 1 + }) + .css(merge(chartStyle, options.labelStyle)) + .add(inputGroup); + inputGroup.offset += label.width + 5; + + // Create an SVG label that shows updated date ranges and and records click events that + // bring in the HTML input. + this[name + 'DateBox'] = dateBox = renderer.label('', inputGroup.offset) + .attr({ + padding: 1, + width: 90, + height: 16, + stroke: 'silver', + 'stroke-width': 1 + }) + .css(merge({ + textAlign: 'center' + }, chartStyle, options.inputStyle)) + .on('click', function () { + rangeSelector[name + 'Input'].focus(); + }) + .add(inputGroup); + inputGroup.offset += dateBox.width + (isMin ? 10 : 0); + - // create the input element - input = createElement('input', { + // Create the HTML input element. This is rendered as 1x1 pixel then set to the right size + // when focused. + this[name + 'Input'] = input = createElement('input', { name: name, className: PREFIX + 'range-selector', type: 'text' }, extend({ - width: '80px', - height: '16px', - border: '1px solid silver', - marginLeft: '5px', - marginRight: isMin ? '5px' : '0', - textAlign: 'center' + position: ABSOLUTE, + border: 0, + width: '1px', // Chrome needs a pixel to see it + height: '1px', + padding: 0, + textAlign: 'center', + fontSize: chartStyle.fontSize, + fontFamily: chartStyle.fontFamily, + top: chart.plotTop + PX // prevent jump on focus in Firefox }, options.inputStyle), div); - - input.onfocus = input.onblur = function (e) { - e = e || window.event || {}; - input.hasFocus = e.type === 'focus'; - rangeSelector.setInputValue(input); + // Blow up the input box + input.onfocus = function () { + css(this, { + left: (inputGroup.translateX + dateBox.x) + PX, + top: inputGroup.translateY + PX, + width: (dateBox.width - 2) + PX, + height: (dateBox.height - 2) + PX, + border: '2px solid silver' + }); + }; + // Hide away the input box + input.onblur = function () { + css(this, { + border: 0, + width: '1px', + height: '1px' + }); + rangeSelector.setInputValue(name); }; // handle changes in the input boxes @@ -16903,25 +18711,33 @@ value = Date.parse(inputValue), extremes = chart.xAxis[0].getExtremes(); - // if the value isn't parsed directly to a value by the browser's Date.parse method, + // If the value isn't parsed directly to a value by the browser's Date.parse method, // like YYYY-MM-DD in IE, try parsing it a different way if (isNaN(value)) { value = inputValue.split('-'); value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2])); } - if (!isNaN(value) && - ((isMin && (value >= extremes.dataMin && value <= rangeSelector.rightBox.HCTime)) || - (!isMin && (value <= extremes.dataMax && value >= rangeSelector.leftBox.HCTime))) - ) { - chart.xAxis[0].setExtremes( - isMin ? value : extremes.min, - isMin ? extremes.max : value - ); + if (!isNaN(value)) { + + // Correct for timezone offset (#433) + if (!defaultOptions.global.useUTC) { + value = value + new Date().getTimezoneOffset() * 60 * 1000; + } + + // Set the extremes + if ((isMin && (value >= extremes.dataMin && value <= rangeSelector.maxInput.HCTime)) || + (!isMin && (value <= extremes.dataMax && value >= rangeSelector.minInput.HCTime))) { + chart.xAxis[0].setExtremes( + isMin ? value : extremes.min, + isMin ? extremes.max : value, + UNDEFINED, + UNDEFINED, + { trigger: 'rangeSelectorInput' } + ); + } } }; - - return input; }, /** @@ -16936,15 +18752,18 @@ chart = rangeSelector.chart, renderer = chart.renderer, container = chart.container, - options = chart.options.rangeSelector, + chartOptions = chart.options, + navButtonOptions = chartOptions.exporting && chart.options.navigation.buttonOptions, + options = chartOptions.rangeSelector, buttons = rangeSelector.buttons, lang = defaultOptions.lang, div = rangeSelector.div, - chartStyle = chart.options.chart.style, + inputGroup = rangeSelector.inputGroup, buttonTheme = options.buttonTheme, inputEnabled = options.inputEnabled !== false, states = buttonTheme && buttonTheme.states, plotLeft = chart.plotLeft, + yAlign, buttonLeft; // create the elements @@ -16985,31 +18804,39 @@ // first create a wrapper outside the container in order to make // the inputs work and make export correct if (inputEnabled) { - rangeSelector.divRelative = div = createElement('div', null, { + rangeSelector.div = div = createElement('div', null, { position: 'relative', height: 0, - fontFamily: chartStyle.fontFamily, - fontSize: chartStyle.fontSize, zIndex: 1 // above container }); container.parentNode.insertBefore(div, container); - // create an absolutely positionied div to keep the inputs - rangeSelector.divAbsolute = rangeSelector.div = div = createElement('div', null, extend({ - position: 'absolute', - top: (chart.plotTop - 25) + 'px', - right: (chart.chartWidth - chart.plotLeft - chart.plotWidth) + 'px' - }, options.inputBoxStyle), div); + // Create the group to keep the inputs + rangeSelector.inputGroup = inputGroup = renderer.g('input-group') + .add(); + inputGroup.offset = 0; - rangeSelector.leftBox = rangeSelector.drawInput('min'); - rangeSelector.rightBox = rangeSelector.drawInput('max'); + rangeSelector.drawInput('min'); + rangeSelector.drawInput('max'); } } - + if (inputEnabled) { - rangeSelector.setInputValue(rangeSelector.leftBox, min); - rangeSelector.setInputValue(rangeSelector.rightBox, max); + + // Update the alignment to the updated spacing box + yAlign = chart.plotTop - 35; + inputGroup.align(extend({ + y: yAlign, + width: inputGroup.offset, + // detect collision with the exporting buttons + x: navButtonOptions && (yAlign < (navButtonOptions.y || 0) + navButtonOptions.height - chartOptions.chart.spacingTop) ? + -40 : 0 + }, options.inputPosition), true, chart.spacingBox); + + // Set or reset the input values + rangeSelector.setInputValue('min', min); + rangeSelector.setInputValue('max', max); } rangeSelector.rendered = true; @@ -17019,44 +18846,54 @@ * Destroys allocated elements. */ destroy: function () { - var rangeSelector = this, - leftBox = rangeSelector.leftBox, - rightBox = rangeSelector.rightBox, - boxSpanElements = rangeSelector.boxSpanElements, - divRelative = rangeSelector.divRelative, - divAbsolute = rangeSelector.divAbsolute, - zoomText = rangeSelector.zoomText; + var minInput = this.minInput, + maxInput = this.maxInput, + chart = this.chart, + blurInputs = this.blurInputs, + key; - removeEvent(rangeSelector.chart.container, MOUSEDOWN, rangeSelector.mouseDownHandler); + removeEvent(chart.container, 'mousedown', blurInputs); + removeEvent(chart, 'resize', blurInputs); // Destroy elements in collections - each([rangeSelector.buttons], function (coll) { - destroyObjectProperties(coll); - }); - - // Destroy zoomText - if (zoomText) { - rangeSelector.zoomText = zoomText.destroy(); - } - + destroyObjectProperties(this.buttons); + // Clear input element events - if (leftBox) { - leftBox.onfocus = leftBox.onblur = leftBox.onchange = null; + if (minInput) { + minInput.onfocus = minInput.onblur = minInput.onchange = null; } - if (rightBox) { - rightBox.onfocus = rightBox.onblur = rightBox.onchange = null; + if (maxInput) { + maxInput.onfocus = maxInput.onblur = maxInput.onchange = null; } - // Discard divs and spans - each([leftBox, rightBox, boxSpanElements.min, boxSpanElements.max, divAbsolute, divRelative], function (item) { - discardElement(item); - }); - - // Null the references - rangeSelector.leftBox = rangeSelector.rightBox = rangeSelector.boxSpanElements = rangeSelector.div = rangeSelector.divAbsolute = rangeSelector.divRelative = null; + // Destroy HTML and SVG elements + for (key in this) { + if (this[key] && key !== 'chart') { + if (this[key].destroy) { // SVGElement + this[key].destroy(); + } else if (this[key].nodeType) { // HTML element + discardElement(this[key]); + } + } + this[key] = null; + } } }; +// Initialize scroller for stock charts +wrap(Chart.prototype, 'init', function (proceed, options, callback) { + + addEvent(this, 'init', function () { + if (this.options.rangeSelector.enabled) { + this.rangeSelector = new RangeSelector(this); + } + }); + + proceed.call(this, options, callback); + +}); + + Highcharts.RangeSelector = RangeSelector; /* **************************************************************************** @@ -17074,7 +18911,7 @@ extremes = chart.xAxis[0].getExtremes(); scroller.render( mathMax(extremes.min, extremes.dataMin), - mathMin(extremes.max, extremes.dataMax) + mathMin(extremes.max, pick(extremes.dataMax, Number.MAX_VALUE)) ); } @@ -17093,7 +18930,6 @@ function destroyEvents() { if (scroller) { - removeEvent(chart, 'resize', renderScroller); removeEvent(chart.xAxis[0], 'afterSetExtremes', afterSetExtremesHandlerScroller); } if (rangeSelector) { @@ -17107,8 +18943,14 @@ // redraw the scroller on setExtremes addEvent(chart.xAxis[0], 'afterSetExtremes', afterSetExtremesHandlerScroller); - // redraw the scroller chart resize - addEvent(chart, 'resize', renderScroller); + // redraw the scroller on chart resize or box resize + wrap(chart, 'drawChartBox', function (proceed) { + var isDirtyBox = this.isDirtyBox; + proceed.call(this); + if (isDirtyBox) { + renderScroller(); + } + }); // do it now renderScroller(); @@ -17139,21 +18981,20 @@ enabled: false, states: { hover: { - enabled: true, radius: 5 } } }, // gapSize: 0, - shadow: false, states: { hover: { lineWidth: 2 } - }, - dataGrouping: { - enabled: true } + }, + columnOptions = { + shadow: false, + borderWidth: 0 }; // apply X axis options to both single and multi y axes @@ -17197,7 +19038,8 @@ options = merge({ chart: { - panning: true + panning: true, + pinchType: 'x' }, navigator: { enabled: true @@ -17224,19 +19066,19 @@ spline: lineOptions, area: lineOptions, areaspline: lineOptions, - column: { - shadow: false, - borderWidth: 0, - dataGrouping: { - enabled: true - } - } + arearange: lineOptions, + areasplinerange: lineOptions, + column: columnOptions, + columnrange: columnOptions, + candlestick: columnOptions, + ohlc: columnOptions } }, options, // user's options { // forced options + _stock: true, // internal flag chart: { inverted: false } @@ -17248,6 +19090,18 @@ return new Chart(options, callback); }; +// Implement the pinchType option +wrap(Pointer.prototype, 'init', function (proceed, chart, options) { + + var pinchType = options.chart.pinchType || ''; + + proceed.call(this, chart, options); + + // Pinch status + this.pinchX = this.pinchHor = pinchType.indexOf('x') !== -1; + this.pinchY = this.pinchVert = pinchType.indexOf('y') !== -1; +}); + /* **************************************************************************** * Start value compare logic * @@ -17264,30 +19118,40 @@ */ seriesProto.init = function () { - // call base method + // Call base method seriesInit.apply(this, arguments); - // local variables - var series = this, - compare = series.options.compare; - - if (compare) { - series.modifyValue = function (value, point) { - var compareValue = this.compareValue; - - // get the modified value - value = compare === 'value' ? - value - compareValue : // compare value - value = 100 * (value / compareValue) - 100; // compare percent - - // record for tooltip etc. - if (point) { - point.change = value; - } + // Set comparison mode + this.setCompare(this.options.compare); +}; + +/** + * The setCompare method can be called also from the outside after render time + */ +seriesProto.setCompare = function (compare) { + + // Set or unset the modifyValue method + this.modifyValue = (compare === 'value' || compare === 'percent') ? function (value, point) { + var compareValue = this.compareValue; + + // get the modified value + value = compare === 'value' ? + value - compareValue : // compare value + value = 100 * (value / compareValue) - 100; // compare percent - return value; - }; - } + // record for tooltip etc. + if (point) { + point.change = value; + } + + return value; + } : null; + + // Mark dirty + if (this.chart.hasRendered) { + this.isDirty = true; + } + }; /** @@ -17320,6 +19184,20 @@ }; /** + * Add a utility method, setCompare, to the Y axis + */ +Axis.prototype.setCompare = function (compare, redraw) { + if (!this.isXAxis) { + each(this.series, function (series) { + series.setCompare(compare); + }); + if (pick(redraw, true)) { + this.chart.redraw(); + } + } +}; + +/** * Extend the tooltip formatter by adding support for the point.change variable * as well as the changeDecimals option */ @@ -17397,19 +19275,18 @@ each(axis.series, function (series, i) { - if (series.visible !== false) { + if (series.visible !== false && series.takeOrdinalPosition !== false) { // concatenate the processed X data into the existing positions, or the empty array ordinalPositions = ordinalPositions.concat(series.processedXData); len = ordinalPositions.length; - // if we're dealing with more than one series, remove duplicates - if (i && len) { - - ordinalPositions.sort(function (a, b) { - return a - b; // without a custom function it is sorted as strings - }); + // remove duplicates (#1588) + ordinalPositions.sort(function (a, b) { + return a - b; // without a custom function it is sorted as strings + }); + if (len) { i = len - 1; while (i--) { if (ordinalPositions[i] === ordinalPositions[i + 1]) { @@ -17670,6 +19547,10 @@ }); median = distances[mathFloor(len / 2)]; + // Compensate for series that don't extend through the entire axis extent. #1675. + xMin = mathMax(xMin, processedXData[0]); + xMax = mathMin(xMax, processedXData[len - 1]); + // Return the factor needed for data grouping return (len * median) / (xMax - xMin); }; @@ -17708,11 +19589,12 @@ posLength, outsideMax, groupPositions = [], + lastGroupPosition = -Number.MAX_VALUE, tickPixelIntervalOption = xAxis.options.tickPixelInterval; // The positions are not always defined, for example for ordinal positions when data - // has regular interval - if (!positions || min === UNDEFINED) { + // has regular interval (#1557) + if (!positions || positions.length === 1 || min === UNDEFINED) { return getTimeTicks(normalizedInterval, min, max, startOfWeek); } @@ -17732,10 +19614,19 @@ // For each segment, calculate the tick positions from the getTimeTicks utility // function. The interval will be the same regardless of how long the segment is. - segmentPositions = getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek); - - groupPositions = groupPositions.concat(segmentPositions); - + if (positions[end] > lastGroupPosition) { // #1475 + segmentPositions = getTimeTicks(normalizedInterval, positions[start], positions[end], startOfWeek); + + // Prevent duplicate groups, for example for multiple segments within one larger time frame (#1475) + while (segmentPositions.length && segmentPositions[0] <= lastGroupPosition) { + segmentPositions.shift(); + } + if (segmentPositions.length) { + lastGroupPosition = segmentPositions[segmentPositions.length - 1]; + } + + groupPositions = groupPositions.concat(segmentPositions); + } // Set start of next segment start = end + 1; } @@ -17836,7 +19727,6 @@ } } } - return groupPositions; }; @@ -17912,7 +19802,7 @@ // Apply it if it is within the available data range if (newMin > mathMin(extremes.dataMin, min) && newMax < mathMax(dataMax, max)) { - xAxis.setExtremes(newMin, newMax, true, false); + xAxis.setExtremes(newMin, newMax, true, false, { trigger: 'pan' }); } chart.mouseDownX = chartX; // set new reference for next run @@ -17974,23 +19864,28 @@ // Constructors Axis: Axis, - CanVGRenderer: CanVGRenderer, Chart: Chart, Color: Color, Legend: Legend, + Pointer: Pointer, Point: Point, Tick: Tick, Tooltip: Tooltip, Renderer: Renderer, Series: Series, + SVGElement: SVGElement, SVGRenderer: SVGRenderer, - VMLRenderer: VMLRenderer, // Various + arrayMin: arrayMin, + arrayMax: arrayMax, + charts: charts, dateFormat: dateFormat, + format: format, pathAnim: pathAnim, getOptions: getOptions, hasBidiBug: hasBidiBug, + isTouchDevice: isTouchDevice, numberFormat: numberFormat, seriesTypes: seriesTypes, setOptions: setOptions, @@ -18007,7 +19902,11 @@ splat: splat, extendClass: extendClass, pInt: pInt, - product: 'Highstock', - version: '1.1.6' + wrap: wrap, + svg: hasSVG, + canvas: useCanVG, + vml: !hasSVG && !useCanVG, + product: PRODUCT, + version: VERSION }); }()); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/annotations.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/annotations.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/annotations.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/annotations.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,7 @@ +(function(f,A){function k(a){return typeof a==="number"}function l(a){return a!==B&&a!==null}var B,n,o,w=f.Chart,r=f.extend,x=f.each,y;y={xAxis:0,yAxis:0,title:{style:{},text:"",x:0,y:0},shape:{params:{stroke:"#000000",fill:"transparent",strokeWidth:2}}};o=["path","rect","circle"];n={top:0,left:0,center:0.5,middle:0.5,bottom:1,right:1};var s=A.inArray,C=f.merge,z=function(){this.init.apply(this,arguments)};z.prototype={init:function(a,d){this.chart=a;this.options=C({},y,d)},render:function(a){var d= +this.chart,c=this.chart.renderer,b=this.group,m=this.title,e=this.shape,g=this.options,f=g.title,i=g.shape;if(!b)b=this.group=c.g();if(!m&&f)m=this.title=c.label(f),m.add(b);if(!e&&i&&s(i.type,o)!==-1)e=this.shape=c[g.shape.type](i.params),e.add(b);b.add(d.annotations.group);this.linkObjects();a!==!1&&this.redraw()},redraw:function(){var a=this.options,d=this.chart,c=this.group,b=this.title,m=this.shape,e=this.linkedObject,g=d.xAxis[a.xAxis],o=d.yAxis[a.yAxis],i=a.width,t=a.height,u=n[a.anchorY], +v=n[a.anchorX],h,q,j,p;if(e)h=e instanceof f.Point?"point":e instanceof f.Series?"series":null,h==="point"?(a.xValue=e.x,a.yValue=e.y,q=e.series):h==="series"&&(q=e),c.visibility!==q.group.visibility&&c.attr({visibility:q.group.visibility});e=(l(a.xValue)?g.toPixels(a.xValue+g.minPointOffset):a.x)-g.minPixelPadding;h=l(a.yValue)?o.toPixels(a.yValue):a.y;if(!isNaN(e)&&!isNaN(h)&&k(e)&&k(h)){b&&(b.attr(a.title),b.css(a.title.style));if(m){b=r({},a.shape.params);if(a.units==="values"){for(j in b)s(j, +["width","x"])>-1?b[j]=g.translate(b[j]):s(j,["height","y"])>-1&&(b[j]=o.translate(b[j]));b.width&&(b.width-=g.toPixels(0)-g.left);b.x&&(b.x+=g.minPixelPadding)}m.attr(b)}c.bBox=null;if(!k(i))p=c.getBBox(),i=p.width;if(!k(t))p||(p=c.getBBox()),t=p.height;if(!k(v))v=n.center;if(!k(u))u=n.center;e-=i*v;h-=t*u;d.animation&&l(c.translateX)&&l(c.translateY)?c.animate({translateX:e,translateY:h}):c.translate(e,h)}},destroy:function(){var a=this,d=this.chart.annotations.allItems,c=d.indexOf(a);c>-1&&d.splice(c, +1);x(["title","shape","group"],function(b){a[b]&&(a[b].destroy(),a[b]=null)});a.group=a.title=a.shape=a.chart=a.options=null},update:function(a,d){r(this.options,a);this.linkObjects();this.render(d)},linkObjects:function(){var a=this.chart,d=this.linkedObject,c=d&&(d.id||d.options.id),b=this.options.linkedTo;if(l(b)){if(!l(d)||b!==c)this.linkedObject=a.get(b)}else this.linkedObject=null}};r(w.prototype,{annotations:{add:function(a,d){var c=this.allItems,b=this.chart,f,e;Object.prototype.toString.call(a)=== +"[object Array]"||(a=[a]);for(e=a.length;e--;)f=new z(b,a[e]),c.push(f),f.render(d)},redraw:function(){x(this.allItems,function(a){a.redraw()})}}});w.prototype.callbacks.push(function(a){var d=a.options.annotations,c;c=a.renderer.g("annotations");c.attr({zIndex:7});c.add();a.annotations.allItems=[];a.annotations.chart=a;a.annotations.group=c;Object.prototype.toString.call(d)==="[object Array]"&&d.length>0&&a.annotations.add(a.options.annotations);f.addEvent(a,"redraw",function(){a.annotations.redraw()})})})(Highcharts, +HighchartsAdapter); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/annotations.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/annotations.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/annotations.src.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/annotations.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,354 @@ +(function (Highcharts, HighchartsAdapter) { + +var UNDEFINED, + ALIGN_FACTOR, + ALLOWED_SHAPES, + Chart = Highcharts.Chart, + extend = Highcharts.extend, + each = Highcharts.each, + defaultOptions; + +defaultOptions = { + xAxis: 0, + yAxis: 0, + title: { + style: {}, + text: "", + x: 0, + y: 0 + }, + shape: { + params: { + stroke: "#000000", + fill: "transparent", + strokeWidth: 2 + } + } +}; + +ALLOWED_SHAPES = ["path", "rect", "circle"]; + +ALIGN_FACTOR = { + top: 0, + left: 0, + center: 0.5, + middle: 0.5, + bottom: 1, + right: 1 +}; + + +// Highcharts helper methods +var inArray = HighchartsAdapter.inArray, + merge = Highcharts.merge; + +function isArray(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +} + +function isNumber(n) { + return typeof n === 'number'; +} + +function defined(obj) { + return obj !== UNDEFINED && obj !== null; +} + + +// Define annotation prototype +var Annotation = function () { + this.init.apply(this, arguments); +}; +Annotation.prototype = { + /* + * Initialize the annotation + */ + init: function (chart, options) { + this.chart = chart; + this.options = merge({}, defaultOptions, options); + }, + + /* + * Render the annotation + */ + render: function (redraw) { + var annotation = this, + chart = this.chart, + renderer = annotation.chart.renderer, + group = annotation.group, + title = annotation.title, + shape = annotation.shape, + options = annotation.options, + titleOptions = options.title, + shapeOptions = options.shape; + + if (!group) { + group = annotation.group = renderer.g(); + } + + if (!title && titleOptions) { + title = annotation.title = renderer.label(titleOptions); + title.add(group); + } + + if (!shape && shapeOptions && inArray(shapeOptions.type, ALLOWED_SHAPES) !== -1) { + shape = annotation.shape = renderer[options.shape.type](shapeOptions.params); + shape.add(group); + } + + group.add(chart.annotations.group); + + // link annotations to point or series + annotation.linkObjects(); + + if (redraw !== false) { + annotation.redraw(); + } + }, + + /* + * Redraw the annotation title or shape after options update + */ + redraw: function () { + var options = this.options, + chart = this.chart, + group = this.group, + title = this.title, + shape = this.shape, + linkedTo = this.linkedObject, + xAxis = chart.xAxis[options.xAxis], + yAxis = chart.yAxis[options.yAxis], + width = options.width, + height = options.height, + anchorY = ALIGN_FACTOR[options.anchorY], + anchorX = ALIGN_FACTOR[options.anchorX], + resetBBox = false, + shapeParams, + linkType, + series, + param, + bbox, + x, + y; + + if (linkedTo) { + linkType = (linkedTo instanceof Highcharts.Point) ? 'point' : + (linkedTo instanceof Highcharts.Series) ? 'series' : null; + + if (linkType === 'point') { + options.xValue = linkedTo.x; + options.yValue = linkedTo.y; + series = linkedTo.series; + } else if (linkType === 'series') { + series = linkedTo; + } + + if (group.visibility !== series.group.visibility) { + group.attr({ + visibility: series.group.visibility + }); + } + } + + + // Based on given options find annotation pixel position + x = (defined(options.xValue) ? xAxis.toPixels(options.xValue + xAxis.minPointOffset) : options.x) - xAxis.minPixelPadding; + y = defined(options.yValue) ? yAxis.toPixels(options.yValue) : options.y; + + + if (isNaN(x) || isNaN(y) || !isNumber(x) || !isNumber(y)) { + return; + } + + + if (title) { + title.attr(options.title); + title.css(options.title.style); + resetBBox = true; + } + + if (shape) { + shapeParams = extend({}, options.shape.params); + + if (options.units === 'values') { + for (param in shapeParams) { + if (inArray(param, ['width', 'x']) > -1) { + shapeParams[param] = xAxis.translate(shapeParams[param]); + } else if (inArray(param, ['height', 'y']) > -1) { + shapeParams[param] = yAxis.translate(shapeParams[param]); + } + } + + if (shapeParams.width) { + shapeParams.width -= xAxis.toPixels(0) - xAxis.left; + } + + if (shapeParams.x) { + shapeParams.x += xAxis.minPixelPadding; + } + + } + + resetBBox = true; + shape.attr(shapeParams); + } + + group.bBox = null; + + // If annotation width or height is not defined in options use bounding box size + if (!isNumber(width)) { + bbox = group.getBBox(); + width = bbox.width; + } + + if (!isNumber(height)) { + // get bbox only if it wasn't set before + if (!bbox) { + bbox = group.getBBox(); + } + + height = bbox.height; + } + + // Calculate anchor point + if (!isNumber(anchorX)) { + anchorX = ALIGN_FACTOR.center; + } + + if (!isNumber(anchorY)) { + anchorY = ALIGN_FACTOR.center; + } + + // Translate group according to its dimension and anchor point + x = x - width * anchorX; + y = y - height * anchorY; + + if (chart.animation && defined(group.translateX) && defined(group.translateY)) { + group.animate({ + translateX: x, + translateY: y + }); + } else { + group.translate(x, y); + } + }, + + /* + * Destroy the annotation + */ + destroy: function () { + var annotation = this, + chart = this.chart, + allItems = chart.annotations.allItems, + index = allItems.indexOf(annotation); + + if (index > -1) { + allItems.splice(index, 1); + } + + each(['title', 'shape', 'group'], function (element) { + if (annotation[element]) { + annotation[element].destroy(); + annotation[element] = null; + } + }); + + annotation.group = annotation.title = annotation.shape = annotation.chart = annotation.options = null; + }, + + /* + * Update the annotation with a given options + */ + update: function (options, redraw) { + extend(this.options, options); + + // update link to point or series + this.linkObjects(); + + this.render(redraw); + }, + + linkObjects: function () { + var annotation = this, + chart = annotation.chart, + linkedTo = annotation.linkedObject, + linkedId = linkedTo && (linkedTo.id || linkedTo.options.id), + options = annotation.options, + id = options.linkedTo; + + if (!defined(id)) { + annotation.linkedObject = null; + } else if (!defined(linkedTo) || id !== linkedId) { + annotation.linkedObject = chart.get(id); + } + } +}; + + +// Add annotations methods to chart prototype +extend(Chart.prototype, { + annotations: { + /* + * Unified method for adding annotations to the chart + */ + add: function (options, redraw) { + var annotations = this.allItems, + chart = this.chart, + item, + len; + + if (!isArray(options)) { + options = [options]; + } + + len = options.length; + + while (len--) { + item = new Annotation(chart, options[len]); + annotations.push(item); + item.render(redraw); + } + }, + + /** + * Redraw all annotations, method used in chart events + */ + redraw: function () { + each(this.allItems, function (annotation) { + annotation.redraw(); + }); + } + } +}); + + +// Initialize on chart load +Chart.prototype.callbacks.push(function (chart) { + var options = chart.options.annotations, + group; + + group = chart.renderer.g("annotations"); + group.attr({ + zIndex: 7 + }); + group.add(); + + // initialize empty array for annotations + chart.annotations.allItems = []; + + // link chart object to annotations + chart.annotations.chart = chart; + + // link annotations group element to the chart + chart.annotations.group = group; + + if (isArray(options) && options.length > 0) { + chart.annotations.add(chart.options.annotations); + } + + // update annotations after chart redraw + Highcharts.addEvent(chart, 'redraw', function () { + chart.annotations.redraw(); + }); +}); +}(Highcharts, HighchartsAdapter)); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/canvas-tools.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/canvas-tools.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/canvas-tools.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/canvas-tools.js 2015-01-29 17:00:42.000000000 +0000 @@ -11,10 +11,10 @@ Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/ - Highstock JS v1.1.6 (2012-06-08) + Highstock JS v1.3.2 (2013-06-05) CanVGRenderer Extension module - (c) 2011-2012 Torstein H?nsi, Erik Olsson + (c) 2011-2012 Torstein Hønsi, Erik Olsson License: www.highcharts.com/license */ diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/canvas-tools.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/canvas-tools.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/canvas-tools.src.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/canvas-tools.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -2908,7 +2908,7 @@ }); } }/** - * @license Highstock JS v1.1.6 (2012-06-08) + * @license Highstock JS v1.3.2 (2013-06-05) * CanVGRenderer Extension module * * (c) 2011-2012 Torstein Hønsi, Erik Olsson diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/data.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/data.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/data.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/data.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,16 @@ +/* + Data plugin for Highcharts + + (c) 2012-2013 Torstein Hønsi + Last revision 2012-11-27 + + License: www.highcharts.com/license +*/ +(function(f){var k=f.each,n=function(a){this.init(a)};f.extend(n.prototype,{init:function(a){this.options=a;this.columns=a.columns||this.rowsToColumns(a.rows)||[];this.columns.length?this.dataFound():(this.parseCSV(),this.parseTable(),this.parseGoogleSpreadsheet())},dataFound:function(){this.parseTypes();this.findHeaderRow();this.parsed();this.complete()},parseCSV:function(){var a=this,b=this.options,c=b.csv,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn|| +Number.MAX_VALUE,q=0;c&&(c=c.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split(b.lineDelimiter||"\n"),k(c,function(c,m){var o=a.trim(c),f=o.indexOf("#")===0;m>=e&&m<=g&&!f&&o!==""&&(o=c.split(b.itemDelimiter||","),k(o,function(b,a){a>=h&&a<=l&&(d[a-h]||(d[a-h]=[]),d[a-h][q]=b)}),q+=1)}),this.dataFound())},parseTable:function(){var a=this.options,b=a.table,c=this.columns,d=a.startRow||0,e=a.endRow||Number.MAX_VALUE,g=a.startColumn||0,h=a.endColumn||Number.MAX_VALUE,l;b&&(typeof b==="string"&&(b=document.getElementById(b)), +k(b.getElementsByTagName("tr"),function(a,b){l=0;b>=d&&b<=e&&k(a.childNodes,function(a){if((a.tagName==="TD"||a.tagName==="TH")&&l>=g&&l<=h)c[l]||(c[l]=[]),c[l][b-d]=a.innerHTML,l+=1})}),this.dataFound())},parseGoogleSpreadsheet:function(){var a=this,b=this.options,c=b.googleSpreadsheetKey,d=this.columns,e=b.startRow||0,g=b.endRow||Number.MAX_VALUE,h=b.startColumn||0,l=b.endColumn||Number.MAX_VALUE,f,j;c&&jQuery.getJSON("https://spreadsheets.google.com/feeds/cells/"+c+"/"+(b.googleSpreadsheetWorksheet|| +"od6")+"/public/values?alt=json-in-script&callback=?",function(b){var b=b.feed.entry,c,k=b.length,n=0,p=0,i;for(i=0;i=h&&i<=l)d[i-h]=[],d[i-h].length=Math.min(p,g-e);for(i=0;i=h&&j<=l&&f>=e&&f<=g)d[j-h][f-e]=c.content.$t;a.dataFound()})},findHeaderRow:function(){k(this.columns,function(){});this.headerRow=0},trim:function(a){return typeof a==="string"?a.replace(/^\s+|\s+$/g, +""):a},parseTypes:function(){for(var a=this.columns,b=a.length,c,d,e,g;b--;)for(c=a[b].length;c--;)d=a[b][c],e=parseFloat(d),g=this.trim(d),g==e?(a[b][c]=e,e>31536E6?a[b].isDatetime=!0:a[b].isNumeric=!0):(d=this.parseDate(d),b===0&&typeof d==="number"&&!isNaN(d)?(a[b][c]=d,a[b].isDatetime=!0):a[b][c]=g===""?null:g)},dateFormats:{"YYYY-mm-dd":{regex:"^([0-9]{4})-([0-9]{2})-([0-9]{2})$",parser:function(a){return Date.UTC(+a[1],a[2]-1,+a[3])}}},parseDate:function(a){var b=this.options.parseDate,c,d, +e;b&&(c=b(a));if(typeof a==="string")for(d in this.dateFormats)b=this.dateFormats[d],(e=a.match(b.regex))&&(c=b.parser(e));return c},rowsToColumns:function(a){var b,c,d,e,g;if(a){g=[];c=a.length;for(b=0;b1&&(d=a.shift(),this.headerRow===0&& +d.shift(),(b=d.isNumeric||d.isDatetime)||(c=d),d.isDatetime&&(e="datetime"));h=[];for(j=0;j> + * A two-dimensional array representing the input data on tabular form. This input can + * be used when the data is already parsed, for example from a grid view component. + * Each cell can be a string or number. If not switchRowsAndColumns is set, the columns + * are interpreted as series. See also the rows option. + * + * - complete : Function(chartOptions) + * The callback that is evaluated when the data is finished loading, optionally from an + * external source, and parsed. The first argument passed is a finished chart options + * object, containing series and an xAxis with categories if applicable. Thise options + * can be extended with additional options and passed directly to the chart constructor. + * + * - csv : String + * A comma delimited string to be parsed. Related options are startRow, endRow, startColumn + * and endColumn to delimit what part of the table is used. The lineDelimiter and + * itemDelimiter options define the CSV delimiter formats. + * + * - endColumn : Integer + * In tabular input data, the first row (indexed by 0) to use. Defaults to the last + * column containing data. + * + * - endRow : Integer + * In tabular input data, the last row (indexed by 0) to use. Defaults to the last row + * containing data. + * + * - googleSpreadsheetKey : String + * A Google Spreadsheet key. See https://developers.google.com/gdata/samples/spreadsheet_sample + * for general information on GS. + * + * - googleSpreadsheetWorksheet : String + * The Google Spreadsheet worksheet. The available id's can be read from + * https://spreadsheets.google.com/feeds/worksheets/{key}/public/basic + * + * - itemDelimiter : String + * Item or cell delimiter for parsing CSV. Defaults to ",". + * + * - lineDelimiter : String + * Line delimiter for parsing CSV. Defaults to "\n". + * + * - parsed : Function + * A callback function to access the parsed columns, the two-dimentional input data + * array directly, before they are interpreted into series data and categories. + * + * - parseDate : Function + * A callback function to parse string representations of dates into JavaScript timestamps. + * Return an integer on success. + * + * - rows : Array> + * The same as the columns input option, but defining rows intead of columns. + * + * - startColumn : Integer + * In tabular input data, the first column (indexed by 0) to use. + * + * - startRow : Integer + * In tabular input data, the first row (indexed by 0) to use. + * + * - table : String|HTMLElement + * A HTML table or the id of such to be parsed as input data. Related options ara startRow, + * endRow, startColumn and endColumn to delimit what part of the table is used. + */ + +// JSLint options: +/*global jQuery */ + +(function (Highcharts) { + + // Utilities + var each = Highcharts.each; + + + // The Data constructor + var Data = function (options) { + this.init(options); + }; + + // Set the prototype properties + Highcharts.extend(Data.prototype, { + + /** + * Initialize the Data object with the given options + */ + init: function (options) { + this.options = options; + this.columns = options.columns || this.rowsToColumns(options.rows) || []; + + // No need to parse or interpret anything + if (this.columns.length) { + this.dataFound(); + + // Parse and interpret + } else { + + // Parse a CSV string if options.csv is given + this.parseCSV(); + + // Parse a HTML table if options.table is given + this.parseTable(); + + // Parse a Google Spreadsheet + this.parseGoogleSpreadsheet(); + } + + }, + + dataFound: function () { + // Interpret the values into right types + this.parseTypes(); + + // Use first row for series names? + this.findHeaderRow(); + + // Handle columns if a handleColumns callback is given + this.parsed(); + + // Complete if a complete callback is given + this.complete(); + + }, + + /** + * Parse a CSV input string + */ + parseCSV: function () { + var self = this, + options = this.options, + csv = options.csv, + columns = this.columns, + startRow = options.startRow || 0, + endRow = options.endRow || Number.MAX_VALUE, + startColumn = options.startColumn || 0, + endColumn = options.endColumn || Number.MAX_VALUE, + lines, + activeRowNo = 0; + + if (csv) { + + lines = csv + .replace(/\r\n/g, "\n") // Unix + .replace(/\r/g, "\n") // Mac + .split(options.lineDelimiter || "\n"); + + each(lines, function (line, rowNo) { + var trimmed = self.trim(line), + isComment = trimmed.indexOf('#') === 0, + isBlank = trimmed === '', + items; + + if (rowNo >= startRow && rowNo <= endRow && !isComment && !isBlank) { + items = line.split(options.itemDelimiter || ','); + each(items, function (item, colNo) { + if (colNo >= startColumn && colNo <= endColumn) { + if (!columns[colNo - startColumn]) { + columns[colNo - startColumn] = []; + } + + columns[colNo - startColumn][activeRowNo] = item; + } + }); + activeRowNo += 1; + } + }); + + this.dataFound(); + } + }, + + /** + * Parse a HTML table + */ + parseTable: function () { + var options = this.options, + table = options.table, + columns = this.columns, + startRow = options.startRow || 0, + endRow = options.endRow || Number.MAX_VALUE, + startColumn = options.startColumn || 0, + endColumn = options.endColumn || Number.MAX_VALUE, + colNo; + + if (table) { + + if (typeof table === 'string') { + table = document.getElementById(table); + } + + each(table.getElementsByTagName('tr'), function (tr, rowNo) { + colNo = 0; + if (rowNo >= startRow && rowNo <= endRow) { + each(tr.childNodes, function (item) { + if ((item.tagName === 'TD' || item.tagName === 'TH') && colNo >= startColumn && colNo <= endColumn) { + if (!columns[colNo]) { + columns[colNo] = []; + } + columns[colNo][rowNo - startRow] = item.innerHTML; + + colNo += 1; + } + }); + } + }); + + this.dataFound(); // continue + } + }, + + /** + * TODO: + * - switchRowsAndColumns + */ + parseGoogleSpreadsheet: function () { + var self = this, + options = this.options, + googleSpreadsheetKey = options.googleSpreadsheetKey, + columns = this.columns, + startRow = options.startRow || 0, + endRow = options.endRow || Number.MAX_VALUE, + startColumn = options.startColumn || 0, + endColumn = options.endColumn || Number.MAX_VALUE, + gr, // google row + gc; // google column + + if (googleSpreadsheetKey) { + jQuery.getJSON('https://spreadsheets.google.com/feeds/cells/' + + googleSpreadsheetKey + '/' + (options.googleSpreadsheetWorksheet || 'od6') + + '/public/values?alt=json-in-script&callback=?', + function (json) { + + // Prepare the data from the spreadsheat + var cells = json.feed.entry, + cell, + cellCount = cells.length, + colCount = 0, + rowCount = 0, + i; + + // First, find the total number of columns and rows that + // are actually filled with data + for (i = 0; i < cellCount; i++) { + cell = cells[i]; + colCount = Math.max(colCount, cell.gs$cell.col); + rowCount = Math.max(rowCount, cell.gs$cell.row); + } + + // Set up arrays containing the column data + for (i = 0; i < colCount; i++) { + if (i >= startColumn && i <= endColumn) { + // Create new columns with the length of either end-start or rowCount + columns[i - startColumn] = []; + + // Setting the length to avoid jslint warning + columns[i - startColumn].length = Math.min(rowCount, endRow - startRow); + } + } + + // Loop over the cells and assign the value to the right + // place in the column arrays + for (i = 0; i < cellCount; i++) { + cell = cells[i]; + gr = cell.gs$cell.row - 1; // rows start at 1 + gc = cell.gs$cell.col - 1; // columns start at 1 + + // If both row and col falls inside start and end + // set the transposed cell value in the newly created columns + if (gc >= startColumn && gc <= endColumn && + gr >= startRow && gr <= endRow) { + columns[gc - startColumn][gr - startRow] = cell.content.$t; + } + } + self.dataFound(); + }); + } + }, + + /** + * Find the header row. For now, we just check whether the first row contains + * numbers or strings. Later we could loop down and find the first row with + * numbers. + */ + findHeaderRow: function () { + var headerRow = 0; + each(this.columns, function (column) { + if (typeof column[0] !== 'string') { + headerRow = null; + } + }); + this.headerRow = 0; + }, + + /** + * Trim a string from whitespace + */ + trim: function (str) { + return typeof str === 'string' ? str.replace(/^\s+|\s+$/g, '') : str; + }, + + /** + * Parse numeric cells in to number types and date types in to true dates. + * @param {Object} columns + */ + parseTypes: function () { + var columns = this.columns, + col = columns.length, + row, + val, + floatVal, + trimVal, + dateVal; + + while (col--) { + row = columns[col].length; + while (row--) { + val = columns[col][row]; + floatVal = parseFloat(val); + trimVal = this.trim(val); + + /*jslint eqeq: true*/ + if (trimVal == floatVal) { // is numeric + /*jslint eqeq: false*/ + columns[col][row] = floatVal; + + // If the number is greater than milliseconds in a year, assume datetime + if (floatVal > 365 * 24 * 3600 * 1000) { + columns[col].isDatetime = true; + } else { + columns[col].isNumeric = true; + } + + } else { // string, continue to determine if it is a date string or really a string + dateVal = this.parseDate(val); + + if (col === 0 && typeof dateVal === 'number' && !isNaN(dateVal)) { // is date + columns[col][row] = dateVal; + columns[col].isDatetime = true; + + } else { // string + columns[col][row] = trimVal === '' ? null : trimVal; + } + } + + } + } + }, + //* + dateFormats: { + 'YYYY-mm-dd': { + regex: '^([0-9]{4})-([0-9]{2})-([0-9]{2})$', + parser: function (match) { + return Date.UTC(+match[1], match[2] - 1, +match[3]); + } + } + }, + // */ + /** + * Parse a date and return it as a number. Overridable through options.parseDate. + */ + parseDate: function (val) { + var parseDate = this.options.parseDate, + ret, + key, + format, + match; + + if (parseDate) { + ret = parseDate(val); + } + + if (typeof val === 'string') { + for (key in this.dateFormats) { + format = this.dateFormats[key]; + match = val.match(format.regex); + if (match) { + ret = format.parser(match); + } + } + } + return ret; + }, + + /** + * Reorganize rows into columns + */ + rowsToColumns: function (rows) { + var row, + rowsLength, + col, + colsLength, + columns; + + if (rows) { + columns = []; + rowsLength = rows.length; + for (row = 0; row < rowsLength; row++) { + colsLength = rows[row].length; + for (col = 0; col < colsLength; col++) { + if (!columns[col]) { + columns[col] = []; + } + columns[col][row] = rows[row][col]; + } + } + } + return columns; + }, + + /** + * A hook for working directly on the parsed columns + */ + parsed: function () { + if (this.options.parsed) { + this.options.parsed.call(this, this.columns); + } + }, + + /** + * If a complete callback function is provided in the options, interpret the + * columns into a Highcharts options object. + */ + complete: function () { + + var columns = this.columns, + hasXData, + categories, + firstCol, + type, + options = this.options, + series, + data, + name, + i, + j; + + + if (options.complete) { + + // Use first column for X data or categories? + if (columns.length > 1) { + firstCol = columns.shift(); + if (this.headerRow === 0) { + firstCol.shift(); // remove the first cell + } + + // Use the first column for categories or X values + hasXData = firstCol.isNumeric || firstCol.isDatetime; + if (!hasXData) { // means type is neither datetime nor linear + categories = firstCol; + } + + if (firstCol.isDatetime) { + type = 'datetime'; + } + } + + // Use the next columns for series + series = []; + for (i = 0; i < columns.length; i++) { + if (this.headerRow === 0) { + name = columns[i].shift(); + } + data = []; + for (j = 0; j < columns[i].length; j++) { + data[j] = columns[i][j] !== undefined ? + (hasXData ? + [firstCol[j], columns[i][j]] : + columns[i][j] + ) : + null; + } + series[i] = { + name: name, + data: data + }; + } + + // Do the callback + options.complete({ + xAxis: { + categories: categories, + type: type + }, + series: series + }); + } + } + }); + + // Register the Data prototype and data function on Highcharts + Highcharts.Data = Data; + Highcharts.data = function (options) { + return new Data(options); + }; + + // Extend Chart.init so that the Chart constructor accepts a new configuration + // option group, data. + Highcharts.wrap(Highcharts.Chart.prototype, 'init', function (proceed, userOptions, callback) { + var chart = this; + + if (userOptions && userOptions.data) { + Highcharts.data(Highcharts.extend(userOptions.data, { + complete: function (dataOptions) { + + // Merge series configs + if (userOptions.series) { + each(userOptions.series, function (series, i) { + userOptions.series[i] = Highcharts.merge(series, dataOptions.series[i]); + }); + } + + // Do the merge + userOptions = Highcharts.merge(dataOptions, userOptions); + + proceed.call(chart, userOptions, callback); + } + })); + } else { + proceed.call(chart, userOptions, callback); + } + }); + +}(Highcharts)); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/exporting.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/exporting.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/exporting.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/exporting.js 2015-01-29 17:00:42.000000000 +0000 @@ -1,23 +1,22 @@ /* - Highstock JS v1.1.6 (2012-06-08) + Highstock JS v1.3.2 (2013-06-05) Exporting module - (c) 2010-2011 Torstein H?nsi + (c) 2010-2013 Torstein Hønsi License: www.highcharts.com/license */ -(function(){function x(a){for(var b=a.length;b--;)typeof a[b]==="number"&&(a[b]=Math.round(a[b])-0.5);return a}var g=Highcharts,y=g.Chart,z=g.addEvent,B=g.removeEvent,r=g.createElement,u=g.discardElement,t=g.css,s=g.merge,k=g.each,n=g.extend,C=Math.max,h=document,D=window,A=h.documentElement.ontouchstart!==void 0,v=g.getOptions();n(v.lang,{downloadPNG:"Download PNG image",downloadJPEG:"Download JPEG image",downloadPDF:"Download PDF document",downloadSVG:"Download SVG vector image",exportButtonTitle:"Export to raster or vector image", -printButtonTitle:"Print the chart"});v.navigation={menuStyle:{border:"1px solid #A0A0A0",background:"#FFFFFF"},menuItemStyle:{padding:"0 5px",background:"none",color:"#303030",fontSize:A?"14px":"11px"},menuItemHoverStyle:{background:"#4572A5",color:"#FFFFFF"},buttonOptions:{align:"right",backgroundColor:{linearGradient:[0,0,0,20],stops:[[0.4,"#F7F7F7"],[0.6,"#E3E3E3"]]},borderColor:"#B0B0B0",borderRadius:3,borderWidth:1,height:20,hoverBorderColor:"#909090",hoverSymbolFill:"#81A7CF",hoverSymbolStroke:"#4572A5", -symbolFill:"#E0E0E0",symbolStroke:"#A0A0A0",symbolX:11.5,symbolY:10.5,verticalAlign:"top",width:24,y:10}};v.exporting={type:"image/png",url:"http://export.highcharts.com/",width:800,buttons:{exportButton:{symbol:"exportIcon",x:-10,symbolFill:"#A8BF77",hoverSymbolFill:"#768F3E",_id:"exportButton",_titleKey:"exportButtonTitle",menuItems:[{textKey:"downloadPNG",onclick:function(){this.exportChart()}},{textKey:"downloadJPEG",onclick:function(){this.exportChart({type:"image/jpeg"})}},{textKey:"downloadPDF", -onclick:function(){this.exportChart({type:"application/pdf"})}},{textKey:"downloadSVG",onclick:function(){this.exportChart({type:"image/svg+xml"})}}]},printButton:{symbol:"printIcon",x:-36,symbolFill:"#B5C9DF",hoverSymbolFill:"#779ABF",_id:"printButton",_titleKey:"printButtonTitle",onclick:function(){this.print()}}}};n(y.prototype,{getSVG:function(a){var b=this,c,d,e,f=s(b.options,a);if(!h.createElementNS)h.createElementNS=function(a,b){return h.createElement(b)};a=r("div",null,{position:"absolute", -top:"-9999em",width:b.chartWidth+"px",height:b.chartHeight+"px"},h.body);n(f.chart,{renderTo:a,forExport:!0});f.exporting.enabled=!1;f.chart.plotBackgroundImage=null;f.series=[];k(b.series,function(a){e=s(a.options,{animation:!1,showCheckbox:!1,visible:a.visible});if(!e.isInternal){if(e&&e.marker&&/^url\(/.test(e.marker.symbol))e.marker.symbol="circle";f.series.push(e)}});c=new Highcharts.Chart(f);k(["xAxis","yAxis"],function(a){k(b[a],function(b,d){var e=c[a][d],f=b.getExtremes(),g=f.userMin,f=f.userMax; -(g!==void 0||f!==void 0)&&e.setExtremes(g,f,!0,!1)})});d=c.container.innerHTML;f=null;c.destroy();u(a);d=d.replace(/zIndex="[^"]+"/g,"").replace(/isShadow="[^"]+"/g,"").replace(/symbolName="[^"]+"/g,"").replace(/jQuery[0-9]+="[^"]+"/g,"").replace(/isTracker="[^"]+"/g,"").replace(/url\([^#]+#/g,"url(#").replace(/.*?$/,"").replace(/ /g,"\u00a0").replace(/­/g, -"\u00ad").replace(//g,'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" ]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});d=d.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");d.match(/ xmlns="/g).length===2&&(d=d.replace(/xmlns="[^"]+"/, -""));return d},exportChart:function(a,b){var c,d=this.getSVG(s(this.options.exporting.chartOptions,b)),a=s(this.options.exporting,a);c=r("form",{method:"post",action:a.url,enctype:"multipart/form-data"},{display:"none"},h.body);k(["filename","type","width","svg"],function(b){r("input",{type:"hidden",name:b,value:{filename:a.filename||"chart",type:a.type,width:a.width,svg:d}[b]},null,c)});c.submit();u(c)},print:function(){var a=this,b=a.container,c=[],d=b.parentNode,e=h.body,f=e.childNodes;if(!a.isPrinting)a.isPrinting= -!0,k(f,function(a,b){if(a.nodeType===1)c[b]=a.style.display,a.style.display="none"}),e.appendChild(b),D.print(),setTimeout(function(){d.appendChild(b);k(f,function(a,b){if(a.nodeType===1)a.style.display=c[b]});a.isPrinting=!1},1E3)},contextMenu:function(a,b,c,d,e,f){var i=this,g=i.options.navigation,h=g.menuItemStyle,o=i.chartWidth,p=i.chartHeight,q="cache-"+a,j=i[q],l=C(e,f),m,w;if(!j)i[q]=j=r("div",{className:"highcharts-"+a},{position:"absolute",zIndex:1E3,padding:l+"px"},i.container),m=r("div", -null,n({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},g.menuStyle),j),w=function(){t(j,{display:"none"})},z(j,"mouseleave",w),k(b,function(a){if(a){var b=r("div",{onmouseover:function(){t(this,g.menuItemHoverStyle)},onmouseout:function(){t(this,h)},innerHTML:a.text||i.options.lang[a.textKey]},n({cursor:"pointer"},h),m);b[A?"ontouchstart":"onclick"]=function(){w();a.onclick.apply(i,arguments)};i.exportDivElements.push(b)}}),i.exportDivElements.push(m, -j),i.exportMenuWidth=j.offsetWidth,i.exportMenuHeight=j.offsetHeight;a={display:"block"};c+i.exportMenuWidth>o?a.right=o-c-e-l+"px":a.left=c-l+"px";d+f+i.exportMenuHeight>p?a.bottom=p-d-l+"px":a.top=d+f-l+"px";t(j,a)},addButton:function(a){function b(){p.attr(l);o.attr(j)}var c=this,d=c.renderer,e=s(c.options.navigation.buttonOptions,a),f=e.onclick,g=e.menuItems,h=e.width,k=e.height,o,p,q,a=e.borderWidth,j={stroke:e.borderColor},l={stroke:e.symbolStroke,fill:e.symbolFill},m=e.symbolSize||12;if(!c.exportDivElements)c.exportDivElements= -[],c.exportSVGElements=[];e.enabled!==!1&&(o=d.rect(0,0,h,k,e.borderRadius,a).align(e,!0).attr(n({fill:e.backgroundColor,"stroke-width":a,zIndex:19},j)).add(),q=d.rect(0,0,h,k,0).align(e).attr({id:e._id,fill:"rgba(255, 255, 255, 0.001)",title:c.options.lang[e._titleKey],zIndex:21}).css({cursor:"pointer"}).on("mouseover",function(){p.attr({stroke:e.hoverSymbolStroke,fill:e.hoverSymbolFill});o.attr({stroke:e.hoverBorderColor})}).on("mouseout",b).on("click",b).add(),g&&(f=function(){b();var a=q.getBBox(); -c.contextMenu("export-menu",g,a.x,a.y,h,k)}),q.on("click",function(){f.apply(c,arguments)}),p=d.symbol(e.symbol,e.symbolX-m/2,e.symbolY-m/2,m,m).align(e,!0).attr(n(l,{"stroke-width":e.symbolStrokeWidth||1,zIndex:20})).add(),c.exportSVGElements.push(o,q,p))},destroyExport:function(){var a,b;for(a=0;a.*?$/,"").replace(/ /g," ").replace(/­/g,"­").replace(//g, +'xlink:href="$1"/>').replace(/id=([^" >]+)/g,'id="$1"').replace(/class=([^" >]+)/g,'class="$1"').replace(/ transform /g," ").replace(/:(path|rect)/g,"$1").replace(/style="([^"]+)"/g,function(a){return a.toLowerCase()});d=d.replace(/(url\(#highcharts-[0-9]+)"/g,"$1").replace(/"/g,"'");d.match(/ xmlns="/g).length===2&&(d=d.replace(/xmlns="[^"]+"/,""));return d},exportChart:function(a,b){var a=a||{},c=this.options.exporting,c=this.getSVG(k({chart:{borderRadius:0}},c.chartOptions,b,{exporting:{sourceWidth:a.sourceWidth|| +c.sourceWidth,sourceHeight:a.sourceHeight||c.sourceHeight}})),a=k(this.options.exporting,a);e.post(a.url,{filename:a.filename||"chart",type:a.type,width:a.width||0,scale:a.scale||2,svg:c})},print:function(){var a=this,b=a.container,c=[],d=b.parentNode,e=i.body,g=e.childNodes;if(!a.isPrinting)a.isPrinting=!0,r(g,function(a,b){if(a.nodeType===1)c[b]=a.style.display,a.style.display="none"}),e.appendChild(b),z.focus(),z.print(),setTimeout(function(){d.appendChild(b);r(g,function(a,b){if(a.nodeType=== +1)a.style.display=c[b]});a.isPrinting=!1},1E3)},contextMenu:function(a,b,c,d,e,g,f){var h=this,q=h.options.navigation,n=q.menuItemStyle,o=h.chartWidth,i=h.chartHeight,A="cache-"+a,l=h[A],k=C(e,g),u,j,s;if(!l)h[A]=l=m("div",{className:"highcharts-"+a},{position:"absolute",zIndex:1E3,padding:k+"px"},h.container),u=m("div",null,p({MozBoxShadow:"3px 3px 10px #888",WebkitBoxShadow:"3px 3px 10px #888",boxShadow:"3px 3px 10px #888"},q.menuStyle),l),j=function(){t(l,{display:"none"});f&&f.setState(0);h.openMenu= +!1},v(l,"mouseleave",function(){s=setTimeout(j,500)}),v(l,"mouseenter",function(){clearTimeout(s)}),r(b,function(a){if(a){var b=a.separator?m("hr",null,null,u):m("div",{onmouseover:function(){t(this,q.menuItemHoverStyle)},onmouseout:function(){t(this,n)},onclick:function(){j();a.onclick.apply(h,arguments)},innerHTML:a.text||h.options.lang[a.textKey]},p({cursor:"pointer"},n),u);h.exportDivElements.push(b)}}),h.exportDivElements.push(u,l),h.exportMenuWidth=l.offsetWidth,h.exportMenuHeight=l.offsetHeight; +a={display:"block"};c+h.exportMenuWidth>o?a.right=o-c-e-k+"px":a.left=c-k+"px";d+g+h.exportMenuHeight>i?a.bottom=i-d-k+"px":a.top=d+g-k+"px";t(l,a);h.openMenu=!0},addButton:function(a){var b=this,c=b.renderer,a=k(b.options.navigation.buttonOptions,a),d=a.onclick,i=a.menuItems,g,f,h={stroke:a.symbolStroke,fill:a.symbolFill},q=a.symbolSize||12;if(!b.btnCount)b.btnCount=0;b.btnCount++;if(!b.exportDivElements)b.exportDivElements=[],b.exportSVGElements=[];if(a.enabled!==!1){var n=a.theme,o=n.states,m= +o&&o.hover,o=o&&o.select,j;delete n.states;d?j=function(){d.apply(b,arguments)}:i&&(j=function(){b.contextMenu("contextmenu",i,f.translateX,f.translateY,f.width,f.height,f);f.setState(2)});a.text&&a.symbol?n.paddingLeft=e.pick(n.paddingLeft,25):a.text||p(n,{width:a.width,height:a.height,padding:0});f=c.button(a.text,0,0,j,n,m,o).attr({title:b.options.lang[a._titleKey],"stroke-linecap":"round"});a.symbol&&(g=c.symbol(a.symbol,a.symbolX-q/2,a.symbolY-q/2,q,q).attr(p(h,{"stroke-width":a.symbolStrokeWidth|| +1,zIndex:1})).add(f));f.add().align(p(a,{width:f.width,x:e.pick(a.x,w)}),!0,"spacingBox");w+=(f.width+a.buttonSpacing)*(a.align==="right"?-1:1);b.exportSVGElements.push(f,g)}},destroyExport:function(a){var a=a.target,b,c;for(b=0;b/g, 'xlink:href="$1"/>') .replace(/id=([^" >]+)/g, 'id="$1"') - .replace(/class=([^" ]+)/g, 'class="$1"') + .replace(/class=([^" >]+)/g, 'class="$1"') .replace(/ transform /g, ' ') .replace(/:(path|rect)/g, '$1') .replace(/style="([^"]+)"/g, function (s) { @@ -308,43 +332,36 @@ * @param {Object} chartOptions Additional chart options for the SVG representation of the chart */ exportChart: function (options, chartOptions) { - var form, - chart = this, - svg = chart.getSVG(merge(chart.options.exporting.chartOptions, chartOptions)); // docs + options = options || {}; + + var chart = this, + chartExportingOptions = chart.options.exporting, + svg = chart.getSVG(merge( + { chart: { borderRadius: 0 } }, + chartExportingOptions.chartOptions, + chartOptions, + { + exporting: { + sourceWidth: options.sourceWidth || chartExportingOptions.sourceWidth, // docs: option and parameter in exportChart() + sourceHeight: options.sourceHeight || chartExportingOptions.sourceHeight // docs + } + } + )); // merge the options options = merge(chart.options.exporting, options); - - // create the form - form = createElement('form', { - method: 'post', - action: options.url, - enctype: 'multipart/form-data' - }, { - display: NONE - }, doc.body); - - // add the values - each(['filename', 'type', 'width', 'svg'], function (name) { - createElement('input', { - type: HIDDEN, - name: name, - value: { - filename: options.filename || 'chart', - type: options.type, - width: options.width, - svg: svg - }[name] - }, null, form); + + // do the post + Highcharts.post(options.url, { + filename: options.filename || 'chart', + type: options.type, + width: options.width || 0, // IE8 fails to post undefined correctly, so use 0 + scale: options.scale || 2, + svg: svg }); - // submit - form.submit(); - - // clean up - discardElement(form); }, - + /** * Print the chart */ @@ -375,6 +392,7 @@ body.appendChild(container); // print + win.focus(); // #1510 win.print(); // allow the browser to prepare before reverting @@ -406,7 +424,7 @@ * @param {Number} width The width of the opener button * @param {Number} height The height of the opener button */ - contextMenu: function (name, items, x, y, width, height) { + contextMenu: function (name, items, x, y, width, height, button) { var chart = this, navOptions = chart.options.navigation, menuItemStyle = navOptions.menuItemStyle, @@ -418,6 +436,7 @@ boxShadow = '3px 3px 10px #888', innerMenu, hide, + hideTimer, menuStyle; // create the menu only the first time @@ -442,33 +461,45 @@ // hide on mouse out hide = function () { css(menu, { display: NONE }); + if (button) { + button.setState(0); + } + chart.openMenu = false; }; - addEvent(menu, 'mouseleave', hide); + // Hide the menu some time after mouse leave (#1357) + addEvent(menu, 'mouseleave', function () { + hideTimer = setTimeout(hide, 500); + }); + addEvent(menu, 'mouseenter', function () { + clearTimeout(hideTimer); + }); // create the items each(items, function (item) { if (item) { - var div = createElement(DIV, { - onmouseover: function () { - css(this, navOptions.menuItemHoverStyle); - }, - onmouseout: function () { - css(this, menuItemStyle); - }, - innerHTML: item.text || chart.options.lang[item.textKey] - }, extend({ - cursor: 'pointer' - }, menuItemStyle), innerMenu); - - div[hasTouch ? 'ontouchstart' : 'onclick'] = function () { - hide(); - item.onclick.apply(chart, arguments); - }; + var element = item.separator ? + createElement('hr', null, null, innerMenu) : + createElement(DIV, { + onmouseover: function () { + css(this, navOptions.menuItemHoverStyle); + }, + onmouseout: function () { + css(this, menuItemStyle); + }, + onclick: function () { + hide(); + item.onclick.apply(chart, arguments); + }, + innerHTML: item.text || chart.options.lang[item.textKey] + }, extend({ + cursor: 'pointer' + }, menuItemStyle), innerMenu); + // Keep references to menu divs to be able to destroy them - chart.exportDivElements.push(div); + chart.exportDivElements.push(element); } }); @@ -495,6 +526,7 @@ } css(menu, menuStyle); + chart.openMenu = true; }, /** @@ -506,21 +538,19 @@ btnOptions = merge(chart.options.navigation.buttonOptions, options), onclick = btnOptions.onclick, menuItems = btnOptions.menuItems, - buttonWidth = btnOptions.width, - buttonHeight = btnOptions.height, - box, symbol, button, - borderWidth = btnOptions.borderWidth, - boxAttr = { - stroke: btnOptions.borderColor - - }, symbolAttr = { stroke: btnOptions.symbolStroke, fill: btnOptions.symbolFill }, - symbolSize = btnOptions.symbolSize || 12; + symbolSize = btnOptions.symbolSize || 12, + menuKey; + + if (!chart.btnCount) { + chart.btnCount = 0; + } + menuKey = chart.btnCount++; // Keeps references to the button elements if (!chart.exportDivElements) { @@ -532,106 +562,96 @@ return; } - // element to capture the click - function revert() { - symbol.attr(symbolAttr); - box.attr(boxAttr); - } - - // the box border - box = renderer.rect( - 0, - 0, - buttonWidth, - buttonHeight, - btnOptions.borderRadius, - borderWidth - ) - //.translate(buttonLeft, buttonTop) // to allow gradients - .align(btnOptions, true) - .attr(extend({ - fill: btnOptions.backgroundColor, - 'stroke-width': borderWidth, - zIndex: 19 - }, boxAttr)).add(); - - // the invisible element to track the clicks - button = renderer.rect( - 0, - 0, - buttonWidth, - buttonHeight, - 0 - ) - .align(btnOptions) - .attr({ - id: btnOptions._id, - fill: 'rgba(255, 255, 255, 0.001)', - title: chart.options.lang[btnOptions._titleKey], - zIndex: 21 - }).css({ - cursor: 'pointer' - }) - .on('mouseover', function () { - symbol.attr({ - stroke: btnOptions.hoverSymbolStroke, - fill: btnOptions.hoverSymbolFill - }); - box.attr({ - stroke: btnOptions.hoverBorderColor - }); - }) - .on('mouseout', revert) - .on('click', revert) - .add(); - - // add the click event - if (menuItems) { - onclick = function () { - revert(); - var bBox = button.getBBox(); - chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight); + + var attr = btnOptions.theme, + states = attr.states, + hover = states && states.hover, + select = states && states.select, + callback; + + delete attr.states; + + if (onclick) { + callback = function () { + onclick.apply(chart, arguments); + }; + + } else if (menuItems) { + callback = function () { + chart.contextMenu( + 'contextmenu', + menuItems, + button.translateX, + button.translateY, + button.width, + button.height, + button + ); + button.setState(2); }; } - /*addEvent(button.element, 'click', function() { - onclick.apply(chart, arguments); - });*/ - button.on('click', function () { - onclick.apply(chart, arguments); - }); - // the icon - symbol = renderer.symbol( - btnOptions.symbol, - btnOptions.symbolX - (symbolSize / 2), - btnOptions.symbolY - (symbolSize / 2), - symbolSize, - symbolSize - ) - .align(btnOptions, true) - .attr(extend(symbolAttr, { - 'stroke-width': btnOptions.symbolStrokeWidth || 1, - zIndex: 20 - })).add(); - // Keep references to the renderer element so to be able to destroy them later. - chart.exportSVGElements.push(box, button, symbol); + if (btnOptions.text && btnOptions.symbol) { + attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25); + + } else if (!btnOptions.text) { + extend(attr, { + width: btnOptions.width, + height: btnOptions.height, + padding: 0 + }); + } + + button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select) + .attr({ + title: chart.options.lang[btnOptions._titleKey], + 'stroke-linecap': 'round' + }); + + if (btnOptions.symbol) { + symbol = renderer.symbol( + btnOptions.symbol, + btnOptions.symbolX - (symbolSize / 2), + btnOptions.symbolY - (symbolSize / 2), + symbolSize, + symbolSize + ) + .attr(extend(symbolAttr, { + 'stroke-width': btnOptions.symbolStrokeWidth || 1, + zIndex: 1 + })).add(button); + } + + button.add() + .align(extend(btnOptions, { + width: button.width, + x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654 + }), true, 'spacingBox'); + + buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1); + + chart.exportSVGElements.push(button, symbol); + }, /** * Destroy the buttons. */ - destroyExport: function () { - var i, - chart = this, + destroyExport: function (e) { + var chart = e.target, + i, elem; // Destroy the extra buttons added for (i = 0; i < chart.exportSVGElements.length; i++) { elem = chart.exportSVGElements[i]; + // Destroy and null the svg/vml elements - elem.onclick = elem.ontouchstart = null; - chart.exportSVGElements[i] = elem.destroy(); + if (elem) { // #1822 + elem.onclick = elem.ontouchstart = null; + chart.exportSVGElements[i] = elem.destroy(); + } } // Destroy the divs for the menu @@ -650,76 +670,27 @@ } }); -/** - * Crisp for 1px stroke width, which is default. In the future, consider a smarter, - * global function. - */ -function crisp(arr) { - var i = arr.length; - while (i--) { - if (typeof arr[i] === 'number') { - arr[i] = Math.round(arr[i]) - 0.5; - } - } - return arr; -} -// Create the export icon -HC.Renderer.prototype.symbols.exportIcon = function (x, y, width, height) { - return crisp([ - M, // the disk - x, y + width, - L, - x + width, y + height, - x + width, y + height * 0.8, - x, y + height * 0.8, - 'Z', - M, // the arrow - x + width * 0.5, y + height * 0.8, - L, - x + width * 0.8, y + height * 0.4, - x + width * 0.4, y + height * 0.4, - x + width * 0.4, y, - x + width * 0.6, y, - x + width * 0.6, y + height * 0.4, - x + width * 0.2, y + height * 0.4, - 'Z' - ]); -}; -// Create the print icon -HC.Renderer.prototype.symbols.printIcon = function (x, y, width, height) { - return crisp([ - M, // the printer - x, y + height * 0.7, - L, - x + width, y + height * 0.7, - x + width, y + height * 0.4, - x, y + height * 0.4, - 'Z', - M, // the upper sheet - x + width * 0.2, y + height * 0.4, - L, - x + width * 0.2, y, - x + width * 0.8, y, - x + width * 0.8, y + height * 0.4, - 'Z', - M, // the lower sheet - x + width * 0.2, y + height * 0.7, - L, - x, y + height, - x + width, y + height, - x + width * 0.8, y + height * 0.7, - 'Z' - ]); +symbols.menu = function (x, y, width, height) { + var arr = [ + M, x, y + 2.5, + L, x + width, y + 2.5, + M, x, y + height / 2 + 0.5, + L, x + width, y + height / 2 + 0.5, + M, x, y + height - 1.5, + L, x + width, y + height - 1.5 + ]; + return arr; }; - // Add the buttons on chart load Chart.prototype.callbacks.push(function (chart) { var n, exportingOptions = chart.options.exporting, buttons = exportingOptions.buttons; + buttonOffset = 0; + if (exportingOptions.enabled !== false) { for (n in buttons) { @@ -733,4 +704,4 @@ }); -}()); +}(Highcharts)); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/funnel.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/funnel.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/funnel.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/funnel.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,12 @@ +/* + + Highcharts funnel module, Beta + + (c) 2010-2012 Torstein Hønsi + + License: www.highcharts.com/license +*/ +(function(l){var B=l.getOptions().plotOptions,q=l.seriesTypes,D=l.merge,C=function(){},z=l.each;B.funnel=D(B.pie,{center:["50%","50%"],width:"90%",neckWidth:"30%",height:"100%",neckHeight:"25%",dataLabels:{connectorWidth:1,connectorColor:"#606060"},size:!0,states:{select:{color:"#C0C0C0",borderColor:"#000000",shadow:!1}}});q.funnel=l.extendClass(q.pie,{type:"funnel",animate:C,translate:function(){var a=function(i,a){return/%$/.test(i)?a*parseInt(i,10)/100:parseInt(i,10)},f=0,d=this.chart,e=d.plotWidth, +d=d.plotHeight,g=0,c=this.options,j=c.center,b=a(j[0],e),j=a(j[0],d),l=a(c.width,e),h,r,m=a(c.height,d),s=a(c.neckWidth,e),u=a(c.neckHeight,d),v=m-u,a=this.data,w,x,q=c.dataLabels.position==="left"?1:0,y,n,A,o,k,t,p;this.getWidthAt=r=function(i){return i>m-u?s:s+(l-s)*((m-u-i)/(m-u))};this.getX=function(i,a){return b+(a?-1:1)*(r(i)/2+c.dataLabels.distance)};this.center=[b,j,m];this.centerX=b;z(a,function(a){f+=a.y});z(a,function(a){p=null;x=f?a.y/f:0;n=g*m;k=n+x*m;h=r(n);y=b-h/2;A=y+h;h=r(k);o=b- +h/2;t=o+h;n>v?(y=o=b-s/2,A=t=b+s/2):k>v&&(p=k,h=r(v),o=b-h/2,t=o+h,k=v);w=["M",y,n,"L",A,n,t,k];p&&w.push(t,p,o,p);w.push(o,k,"Z");a.shapeType="path";a.shapeArgs={d:w};a.percentage=x*100;a.plotX=b;a.plotY=(n+(p||k))/2;a.tooltipPos=[b,a.plotY];a.slice=C;a.half=q;g+=x});this.setTooltipPoints()},drawPoints:function(){var a=this,f=a.options,d=a.chart.renderer;z(a.data,function(e){var g=e.graphic,c=e.shapeArgs;g?g.animate(c):e.graphic=d.path(c).attr({fill:e.color,stroke:f.borderColor,"stroke-width":f.borderWidth}).add(a.group)})}, +drawDataLabels:function(){var a=this.data,f=this.options.dataLabels.distance,d,e,g,c=a.length,j,b;for(this.center[2]-=2*f;c--;)g=a[c],e=(d=g.half)?1:-1,b=g.plotY,j=this.getX(b,d),g.labelPos=[0,b,j+(f-5)*e,b,j+f*e,b,d?"right":"left",0];q.pie.prototype.drawDataLabels.call(this)}})})(Highcharts); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/funnel.src.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/funnel.src.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/modules/funnel.src.js 1970-01-01 00:00:00.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/modules/funnel.src.js 2015-01-29 17:00:42.000000000 +0000 @@ -0,0 +1,284 @@ +/** + * @license + * Highcharts funnel module, Beta + * + * (c) 2010-2012 Torstein Hønsi + * + * License: www.highcharts.com/license + */ + +/*global Highcharts */ +(function (Highcharts) { + +'use strict'; + +// create shortcuts +var defaultOptions = Highcharts.getOptions(), + defaultPlotOptions = defaultOptions.plotOptions, + seriesTypes = Highcharts.seriesTypes, + merge = Highcharts.merge, + noop = function () {}, + each = Highcharts.each; + +// set default options +defaultPlotOptions.funnel = merge(defaultPlotOptions.pie, { + center: ['50%', '50%'], + width: '90%', + neckWidth: '30%', + height: '100%', + neckHeight: '25%', + + dataLabels: { + //position: 'right', + connectorWidth: 1, + connectorColor: '#606060' + }, + size: true, // to avoid adapting to data label size in Pie.drawDataLabels + states: { + select: { + color: '#C0C0C0', + borderColor: '#000000', + shadow: false + } + } +}); + + +seriesTypes.funnel = Highcharts.extendClass(seriesTypes.pie, { + + type: 'funnel', + animate: noop, + + /** + * Overrides the pie translate method + */ + translate: function () { + + var + // Get positions - either an integer or a percentage string must be given + getLength = function (length, relativeTo) { + return (/%$/).test(length) ? + relativeTo * parseInt(length, 10) / 100 : + parseInt(length, 10); + }, + + sum = 0, + series = this, + chart = series.chart, + plotWidth = chart.plotWidth, + plotHeight = chart.plotHeight, + cumulative = 0, // start at top + options = series.options, + center = options.center, + centerX = getLength(center[0], plotWidth), + centerY = getLength(center[0], plotHeight), + width = getLength(options.width, plotWidth), + tempWidth, + getWidthAt, + height = getLength(options.height, plotHeight), + neckWidth = getLength(options.neckWidth, plotWidth), + neckHeight = getLength(options.neckHeight, plotHeight), + neckY = height - neckHeight, + data = series.data, + path, + fraction, + half = options.dataLabels.position === 'left' ? 1 : 0, + + x1, + y1, + x2, + x3, + y3, + x4, + y5; + + // Return the width at a specific y coordinate + series.getWidthAt = getWidthAt = function (y) { + return y > height - neckHeight ? + neckWidth : + neckWidth + (width - neckWidth) * ((height - neckHeight - y) / (height - neckHeight)); + }; + series.getX = function (y, half) { + return centerX + (half ? -1 : 1) * ((getWidthAt(y) / 2) + options.dataLabels.distance); + }; + + // Expose + series.center = [centerX, centerY, height]; + series.centerX = centerX; + + /* + * Individual point coordinate naming: + * + * x1,y1 _________________ x2,y1 + * \ / + * \ / + * \ / + * \ / + * \ / + * x3,y3 _________ x4,y3 + * + * Additional for the base of the neck: + * + * | | + * | | + * | | + * x3,y5 _________ x4,y5 + */ + + + + + // get the total sum + each(data, function (point) { + sum += point.y; + }); + + each(data, function (point) { + // set start and end positions + y5 = null; + fraction = sum ? point.y / sum : 0; + y1 = cumulative * height; + y3 = y1 + fraction * height; + //tempWidth = neckWidth + (width - neckWidth) * ((height - neckHeight - y1) / (height - neckHeight)); + tempWidth = getWidthAt(y1); + x1 = centerX - tempWidth / 2; + x2 = x1 + tempWidth; + tempWidth = getWidthAt(y3); + x3 = centerX - tempWidth / 2; + x4 = x3 + tempWidth; + + // the entire point is within the neck + if (y1 > neckY) { + x1 = x3 = centerX - neckWidth / 2; + x2 = x4 = centerX + neckWidth / 2; + + // the base of the neck + } else if (y3 > neckY) { + y5 = y3; + + tempWidth = getWidthAt(neckY); + x3 = centerX - tempWidth / 2; + x4 = x3 + tempWidth; + + y3 = neckY; + } + + // save the path + path = [ + 'M', + x1, y1, + 'L', + x2, y1, + x4, y3 + ]; + if (y5) { + path.push(x4, y5, x3, y5); + } + path.push(x3, y3, 'Z'); + + // prepare for using shared dr + point.shapeType = 'path'; + point.shapeArgs = { d: path }; + + + // for tooltips and data labels + point.percentage = fraction * 100; + point.plotX = centerX; + point.plotY = (y1 + (y5 || y3)) / 2; + + // Placement of tooltips and data labels + point.tooltipPos = [ + centerX, + point.plotY + ]; + + // Slice is a noop on funnel points + point.slice = noop; + + // Mimicking pie data label placement logic + point.half = half; + + cumulative += fraction; + }); + + + series.setTooltipPoints(); + }, + /** + * Draw a single point (wedge) + * @param {Object} point The point object + * @param {Object} color The color of the point + * @param {Number} brightness The brightness relative to the color + */ + drawPoints: function () { + var series = this, + options = series.options, + chart = series.chart, + renderer = chart.renderer; + + each(series.data, function (point) { + + var graphic = point.graphic, + shapeArgs = point.shapeArgs; + + if (!graphic) { // Create the shapes + point.graphic = renderer.path(shapeArgs). + attr({ + fill: point.color, + stroke: options.borderColor, + 'stroke-width': options.borderWidth + }). + add(series.group); + + } else { // Update the shapes + graphic.animate(shapeArgs); + } + }); + }, + + /** + * Extend the pie data label method + */ + drawDataLabels: function () { + var data = this.data, + labelDistance = this.options.dataLabels.distance, + leftSide, + sign, + point, + i = data.length, + x, + y; + + // In the original pie label anticollision logic, the slots are distributed + // from one labelDistance above to one labelDistance below the pie. In funnels + // we don't want this. + this.center[2] -= 2 * labelDistance; + + // Set the label position array for each point. + while (i--) { + point = data[i]; + leftSide = point.half; + sign = leftSide ? 1 : -1; + y = point.plotY; + x = this.getX(y, leftSide); + + // set the anchor point for data labels + point.labelPos = [ + 0, // first break of connector + y, // a/a + x + (labelDistance - 5) * sign, // second break, right outside point shape + y, // a/a + x + labelDistance * sign, // landing point for connector + y, // a/a + leftSide ? 'right' : 'left', // alignment + 0 // center angle + ]; + } + + seriesTypes.pie.prototype.drawDataLabels.call(this); + } + +}); + + +}(Highcharts)); diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/dark-blue.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/dark-blue.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/dark-blue.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/dark-blue.js 2015-01-29 17:00:42.000000000 +0000 @@ -8,7 +8,7 @@ "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], chart: { backgroundColor: { - linearGradient: [0, 0, 250, 500], + linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, stops: [ [0, 'rgb(48, 48, 96)'], [1, 'rgb(0, 0, 0)'] @@ -132,26 +132,17 @@ navigation: { buttonOptions: { - backgroundColor: { - linearGradient: [0, 0, 0, 20], - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - borderColor: '#000000', - symbolStroke: '#C0C0C0', - hoverSymbolStroke: '#FFFFFF' - } - }, - - exporting: { - buttons: { - exportButton: { - symbolFill: '#55BE3B' - }, - printButton: { - symbolFill: '#7797BE' + symbolStroke: '#DDDDDD', + hoverSymbolStroke: '#FFFFFF', + theme: { + fill: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0.4, '#606060'], + [0.6, '#333333'] + ] + }, + stroke: '#000000' } } }, @@ -160,7 +151,7 @@ rangeSelector: { buttonTheme: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -174,7 +165,7 @@ states: { hover: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#BBB'], [0.6, '#888'] @@ -187,7 +178,7 @@ }, select: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.1, '#000'], [0.3, '#333'] @@ -224,7 +215,7 @@ scrollbar: { barBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -233,7 +224,7 @@ barBorderColor: '#CCC', buttonArrowColor: '#CCC', buttonBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -242,7 +233,7 @@ buttonBorderColor: '#CCC', rifleColor: '#FFF', trackBackgroundColor: { - linearGradient: [0, 0, 0, 10], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, '#000'], [1, '#333'] diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/dark-green.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/dark-green.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/dark-green.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/dark-green.js 2015-01-29 17:00:42.000000000 +0000 @@ -130,28 +130,20 @@ } }, + navigation: { buttonOptions: { - backgroundColor: { - linearGradient: [0, 0, 0, 20], - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - borderColor: '#000000', - symbolStroke: '#C0C0C0', - hoverSymbolStroke: '#FFFFFF' - } - }, - - exporting: { - buttons: { - exportButton: { - symbolFill: '#55BE3B' - }, - printButton: { - symbolFill: '#7797BE' + symbolStroke: '#DDDDDD', + hoverSymbolStroke: '#FFFFFF', + theme: { + fill: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0.4, '#606060'], + [0.6, '#333333'] + ] + }, + stroke: '#000000' } } }, @@ -160,7 +152,7 @@ rangeSelector: { buttonTheme: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -174,7 +166,7 @@ states: { hover: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#BBB'], [0.6, '#888'] @@ -187,7 +179,7 @@ }, select: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.1, '#000'], [0.3, '#333'] @@ -224,7 +216,7 @@ scrollbar: { barBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -233,7 +225,7 @@ barBorderColor: '#CCC', buttonArrowColor: '#CCC', buttonBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -242,7 +234,7 @@ buttonBorderColor: '#CCC', rifleColor: '#FFF', trackBackgroundColor: { - linearGradient: [0, 0, 0, 10], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, '#000'], [1, '#333'] diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/gray.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/gray.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/gray.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/gray.js 2015-01-29 17:00:42.000000000 +0000 @@ -8,7 +8,7 @@ "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], chart: { backgroundColor: { - linearGradient: [0, 0, 0, 400], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, 'rgb(96, 96, 96)'], [1, 'rgb(16, 16, 16)'] @@ -53,6 +53,7 @@ alternateGridColor: null, minorTickInterval: null, gridLineColor: 'rgba(255, 255, 255, .1)', + minorGridLineColor: 'rgba(255,255,255,0.07)', lineWidth: 0, tickWidth: 0, labels: { @@ -86,7 +87,7 @@ }, tooltip: { backgroundColor: { - linearGradient: [0, 0, 0, 50], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, 'rgba(96, 96, 96, .8)'], [1, 'rgba(16, 16, 16, .8)'] @@ -100,6 +101,9 @@ plotOptions: { + series: { + shadow: true + }, line: { dataLabels: { color: '#CCC' @@ -131,26 +135,17 @@ navigation: { buttonOptions: { - backgroundColor: { - linearGradient: [0, 0, 0, 20], - stops: [ - [0.4, '#606060'], - [0.6, '#333333'] - ] - }, - borderColor: '#000000', - symbolStroke: '#C0C0C0', - hoverSymbolStroke: '#FFFFFF' - } - }, - - exporting: { - buttons: { - exportButton: { - symbolFill: '#55BE3B' - }, - printButton: { - symbolFill: '#7797BE' + symbolStroke: '#DDDDDD', + hoverSymbolStroke: '#FFFFFF', + theme: { + fill: { + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, + stops: [ + [0.4, '#606060'], + [0.6, '#333333'] + ] + }, + stroke: '#000000' } } }, @@ -159,7 +154,7 @@ rangeSelector: { buttonTheme: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -173,7 +168,7 @@ states: { hover: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#BBB'], [0.6, '#888'] @@ -186,7 +181,7 @@ }, select: { fill: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.1, '#000'], [0.3, '#333'] @@ -223,7 +218,7 @@ scrollbar: { barBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -232,7 +227,7 @@ barBorderColor: '#CCC', buttonArrowColor: '#CCC', buttonBackgroundColor: { - linearGradient: [0, 0, 0, 20], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0.4, '#888'], [0.6, '#555'] @@ -241,7 +236,7 @@ buttonBorderColor: '#CCC', rifleColor: '#FFF', trackBackgroundColor: { - linearGradient: [0, 0, 0, 10], + linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, stops: [ [0, '#000'], [1, '#333'] diff -Nru openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/grid.js openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/grid.js --- openplacos-0.3+560~lucid1/server/public/lib/highstock/js/themes/grid.js 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/public/lib/highstock/js/themes/grid.js 2015-01-29 17:00:42.000000000 +0000 @@ -7,7 +7,7 @@ colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'], chart: { backgroundColor: { - linearGradient: [0, 0, 500, 500], + linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, stops: [ [0, 'rgb(255, 255, 255)'], [1, 'rgb(240, 240, 255)'] @@ -88,6 +88,14 @@ style: { color: '#99b' } + }, + + navigation: { + buttonOptions: { + theme: { + stroke: '#CCCCCC' + } + } } }; diff -Nru openplacos-0.3+560~lucid1/server/Tracker.rb openplacos-0.4+554~ubuntu10.04.1/server/Tracker.rb --- openplacos-0.3+560~lucid1/server/Tracker.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/Tracker.rb 2015-01-29 17:00:42.000000000 +0000 @@ -4,6 +4,26 @@ @top = top_ @frequency = frequency_ @ifacetoread = list_of_iface_to_read + lastread = Readhour.last + if !lastread.nil? + @lasthour = floor_hour(lastread.created_at) + else + @lasthour = floor_hour(Time.now) #Time.now.hour + end + lastread = Readday.last + if !lastread.nil? + @lastday = floor_day(lastread.created_at) + else + @lastday = floor_day(Time.now) #Time.now.hour + end + end + + def floor_hour(time) + return (time - time.sec - time.min*60) + end + + def floor_day(time) + return (time - time.sec - time.min*60 - time.hour*3600) end def create_thread @@ -11,10 +31,42 @@ Thread.current.abort_on_exception = true loop do sleep @frequency + # create reads + reads = Array.new @ifacetoread.each do |iface| value = Dispatcher.instance.call(iface["name"],iface["iface"], :read,{})[0] - iface["model"].reads.create({:value => value}) + reads << {:value => value,:interface_id => iface["model"].id} + end + Read.create(reads) + + # check if hour has changed + if @lasthour != floor_hour(Time.now) + # if true creates readhour + readhours = Array.new + time = Time.now + @ifacetoread.each do |iface| + value = Read.where(:created_at => (@lasthour)..(@lasthour + 1.hour),:interface_id => iface["model"].id).average('value') + readhours << {:value => value,:interface_id => iface["model"].id, :created_at => (@lasthour + 1.hour) } if !value.nil? + #FIXME : change the time of the record + end + Readhour.create(readhours) + @lasthour = floor_hour(time) + end + + # check if day has changed + if @lastday != floor_day(Time.now) + # creates readday + readdays = Array.new + time = Time.now + @ifacetoread.each do |iface| + value = Readhour.where(:created_at => (@lastday)..(@lastday + 1.day),:interface_id => iface["model"].id).average('value') + readdays << {:value => value,:interface_id => iface["model"].id, :created_at => (@lastday + 1.day)} if !value.nil? + #FIXME : change the time of the record + end + Readday.create(readdays) + @lastday = floor_day(time) end + end end end diff -Nru openplacos-0.3+560~lucid1/server/views/view_interface.haml openplacos-0.4+554~ubuntu10.04.1/server/views/view_interface.haml --- openplacos-0.3+560~lucid1/server/views/view_interface.haml 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/views/view_interface.haml 2015-01-29 17:00:42.000000000 +0000 @@ -6,22 +6,61 @@ var opos = new OposClient("#{base_url }","salut",['read','write']); opos.set_token(" "); - opos.get( '/ressources#{obj_name}' ,{'iface' :'#{interface}', 'start_time' : ''}, function(data) { - + opos.get( '/ressources#{obj_name}' ,{'iface' :'#{interface}', 'start_time' : '', 'end_time' : ''}, function(data) { + data = [].concat(data, [[Date.now(),null]]); + Highcharts.setOptions({ // This is for all plots, change Date axis to local timezone + global : { + useUTC : false + } + }); // Create the chart - window.chart = new Highcharts.StockChart({ - chart: { - renderTo: 'container' - }, + $('#container').highcharts('StockChart', { + chart : { + zoomType: 'xy' + }, - rangeSelector: { - selected: 1 + rangeSelector : { + buttons: [{ + type: 'hour', + count: 1, + text: '1h' + }, { + type: 'day', + count: 1, + text: '1d' + }, { + type: 'month', + count: 1, + text: '1m' + }, { + type: 'year', + count: 1, + text: '1y' + }, { + type: 'all', + text: 'All' + }], + inputEnabled: false, // it supports only days + selected : 4 // all + }, + navigator : { + adaptToUpdatedData: false, + series : { + data : data + } }, + scrollbar: { + liveRedraw: false + }, title: { text: "#{obj_name}" }, - + xAxis : { + events : { + afterSetExtremes : afterSetExtremes + } + }, series: [{ name: "#{obj_name}", data: data, @@ -33,3 +72,105 @@ }); }); }); + /** + * Load new data depending on the selected min and max + */ + function afterSetExtremes(e) { + var opos = new OposClient("#{base_url }","salut",['read','write']); + opos.set_token(" "); + var url, + currentExtremes = this.getExtremes(), + range = e.max - e.min; + var chart = $('#container').highcharts(); + chart.showLoading('Loading data from server...'); + opos.get( '/ressources#{obj_name}' ,{'iface' :'#{interface}', 'start_time' : (e.min), 'end_time' : (e.max) }, function(data) { + chart.series[0].setData(data); + chart.hideLoading(); + }); + + } +#container2{:style => "height: 500px"} +:javascript + $(function () { + var opos = new OposClient("#{base_url }","salut",['read','write']); + opos.set_token(" "); + + $(document).ready(function() { + Highcharts.setOptions({ + global: { + useUTC: false + } + }); + + var chart; + $('#container2').highcharts({ + chart: { + type: 'spline', + animation: Highcharts.svg, // don't animate in old IE + marginRight: 10, + events: { + load: function() { + + // set up the updating of the chart each second + var series = this.series[0]; + setInterval(function() { + opos.get( '/ressources#{obj_name}' ,{'iface' :'#{interface}'}, function(data) { + var x = (new Date()).getTime(), // current time + y = data['value']; + series.addPoint([x, y], true, true); + }); + }, 1000); + } + } + }, + title: { + text: 'Live data' + }, + xAxis: { + type: 'datetime', + tickPixelInterval: 150 + }, + yAxis: { + title: { + text: 'Value' + }, + plotLines: [{ + value: 0, + width: 1, + color: '#808080' + }] + }, + tooltip: { + formatter: function() { + return ''+ this.series.name +'
    '+ + Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) +'
    '+ + Highcharts.numberFormat(this.y, 2); + } + }, + legend: { + enabled: false + }, + exporting: { + enabled: false + }, + series: [{ + name: 'Random data', + data: (function() { + // generate an array of random data + var data = [], + time = (new Date()).getTime(), + i; + + for (i = -59; i <= 0; i++) { + data.push({ + x: time + i * 1000, + y: null + }); + } + return data; + })() + }] + }); + }); + + }); diff -Nru openplacos-0.3+560~lucid1/server/WebServer.rb openplacos-0.4+554~ubuntu10.04.1/server/WebServer.rb --- openplacos-0.3+560~lucid1/server/WebServer.rb 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/server/WebServer.rb 2015-01-29 17:00:42.000000000 +0000 @@ -34,7 +34,20 @@ end def read_history(path_,iface_,start_,end_) - Resource.find_by_name(path_).interfaces.find_by_name(iface_).reads.collect do |r| + ifaceModel = Resource.find_by_name(path_).interfaces.find_by_name(iface_) + if (end_.empty? or start_.empty?) + end_ = Time.now.to_i*1000 + start_ = Read.find(:first, :conditions =>{:interface_id => ifaceModel.id}).created_at.to_i*1000 + end + range = (end_.to_i - start_.to_i)/1000 + if range < 1.day + reads = Read.where(:created_at => Time.at(start_.to_i/1000)..Time.at(end_.to_i/1000),:interface_id => ifaceModel.id) + elsif range < 7.day + reads = Readhour.where(:created_at => Time.at(start_.to_i/1000)..Time.at(end_.to_i/1000),:interface_id => ifaceModel.id) + else + reads = Readday.where(:created_at => Time.at(start_.to_i/1000)..Time.at(end_.to_i/1000),:interface_id => ifaceModel.id) + end + reads.collect do |r| [r.created_at.to_i*1000, r.value] end end @@ -120,13 +133,13 @@ # For register client (User point of view) get '/oauth/apps/new' do - @client = OAuth2::Model::Client.new + @client = Songkick::OAuth2::Model::Client.new haml :new_client end # Post method for register a client post '/oauth/apps.?:format?' do - @client = OAuth2::Model::Client.new(params) + @client = Songkick::OAuth2::Model::Client.new(params) if params[:format]=="json" content_type :json @client.save ? {"client_id" => @client.client_id, "client_secret" => @client.client_secret}.to_json : @client.errors.full_messages.to_json diff -Nru openplacos-0.3+560~lucid1/setup_files/openplacos.service openplacos-0.4+554~ubuntu10.04.1/setup_files/openplacos.service --- openplacos-0.3+560~lucid1/setup_files/openplacos.service 2013-03-07 19:38:45.000000000 +0000 +++ openplacos-0.4+554~ubuntu10.04.1/setup_files/openplacos.service 2015-01-29 17:00:42.000000000 +0000 @@ -3,9 +3,9 @@ Wants=dbus.service [Service] -ExecStart= /bin/sh -c "dbus-launch /usr/bin/openplacos-server -i /tmp/ -f /etc/openplacos.conf &" +ExecStart= /bin/sh -c "dbus-launch /usr/bin/openplacos-server -a -i /var/run/openplacos/ -f /etc/openplacos.conf --db-path /usr/share/openplacos/database.db &" Type=forking -PIDFile=/tmp/openplacos.pid +PIDFile=/var/run/openplacos/openplacos.pid User=openplacos [Install]