From 4c411e1ea7221ff92b438c4039e1431d2add611c Mon Sep 17 00:00:00 2001 From: lz <18519020056@139.com> Date: Wed, 17 Jan 2018 23:13:11 +0800 Subject: [PATCH] message --- TrustStudy.iml | 88 + pom.xml | 410 + .../java/trust/controller/TestController.java | 45 + src/main/java/trust/entity/CountHelper.java | 35 + src/main/java/trust/entity/Record.java | 55 + src/main/java/trust/entity/Result.java | 26 + src/main/java/trust/entity/Ser.java | 89 + .../java/trust/entity/TrustMeasureCustom.java | 25 + src/main/java/trust/entity/Usage.java | 34 + src/main/java/trust/entity/User.java | 71 + .../java/trust/mapper/CountHelperMapper.java | 14 + .../java/trust/mapper/CountHelperMapper.xml | 25 + src/main/java/trust/mapper/RecordMapper.java | 16 + src/main/java/trust/mapper/RecordMapper.xml | 48 + src/main/java/trust/mapper/ServiceMapper.java | 14 + src/main/java/trust/mapper/ServiceMapper.xml | 35 + src/main/java/trust/mapper/UsageMapper.java | 13 + src/main/java/trust/mapper/UsageMapper.xml | 26 + src/main/java/trust/mapper/UserMapper.java | 14 + src/main/java/trust/mapper/UserMapper.xml | 33 + src/main/java/trust/role/ConsumerService.java | 13 + src/main/java/trust/role/PlatformService.java | 13 + src/main/java/trust/role/ProviderService.java | 10 + src/main/java/trust/role/Serve.java | 78 + .../trust/role/impl/ConsumerServiceImpl.java | 80 + .../trust/role/impl/PlatformServiceImpl.java | 123 + .../trust/role/impl/ProviderServiceImpl.java | 32 + src/main/java/trust/service/TestService.java | 16 + .../trust/service/impl/TestServiceImpl.java | 135 + src/main/resources/config.properties | 6 + src/main/resources/log4j.properties | 34 + src/main/resources/mybatis-config.xml | 53 + src/main/resources/spring-dataSource.xml | 120 + src/main/resources/spring-mvc.xml | 110 + src/main/resources/spring.xml | 53 + src/main/test/BasicGenerator.java | 20 + src/main/test/BasicGeneratorDemo.java | 17 + src/main/test/BasicOperation.java | 22 + src/main/test/CountedObject.java | 8 + src/main/test/DelegatingVehicleTracker.java | 53 + src/main/test/Ensemble.java | 16 + src/main/test/ExtendedOperation.java | 50 + src/main/test/Father.java | 37 + src/main/test/Generator.java | 4 + src/main/test/Herb.java | 40 + .../test/Item41/CollectionClassifier.java | 29 + src/main/test/Item41/Overriding.java | 23 + src/main/test/Item41/SetList.java | 23 + src/main/test/MonitorVehicleTracker.java | 61 + src/main/test/MutablePoint.java | 20 + src/main/test/Operation.java | 4 + src/main/test/Phase.java | 48 + src/main/test/PojoBug.java | 14 + src/main/test/Stack.java | 44 + src/main/test/Sub.java | 19 + src/main/test/Super.java | 8 + src/main/test/TestSum.java | 16 + src/main/test/Testsyn.java | 17 + src/main/test/ThreadA.java | 14 + src/main/test/aaa.java | 23 + src/main/test/concurrent/Beer.java | 48 + .../test/concurrent/LaunderThrowable.java | 24 + src/main/test/concurrent/PrimeProducer.java | 33 + src/main/test/concurrent/TestHarness.java | 41 + src/main/test/concurrent/TestIntterupt.java | 51 + src/main/test/sdadsad.java | 55 + src/main/test/test.java | 16 + src/main/test/testThread.java | 17 + src/main/webapp/WEB-INF/views/echarts.js | 63738 ++++++++++++++++ src/main/webapp/WEB-INF/views/macarons.js | 198 + .../webapp/WEB-INF/views/performanceList.jsp | 377 + src/main/webapp/WEB-INF/views/result.jsp | 130 + src/main/webapp/WEB-INF/web.xml | 66 + src/main/webapp/index.jsp | 5 + src/main/webapp/static/echarts.js | 63738 ++++++++++++++++ src/main/webapp/static/macarons.js | 198 + 76 files changed, 131357 insertions(+) create mode 100644 TrustStudy.iml create mode 100644 pom.xml create mode 100644 src/main/java/trust/controller/TestController.java create mode 100644 src/main/java/trust/entity/CountHelper.java create mode 100644 src/main/java/trust/entity/Record.java create mode 100644 src/main/java/trust/entity/Result.java create mode 100644 src/main/java/trust/entity/Ser.java create mode 100644 src/main/java/trust/entity/TrustMeasureCustom.java create mode 100644 src/main/java/trust/entity/Usage.java create mode 100644 src/main/java/trust/entity/User.java create mode 100644 src/main/java/trust/mapper/CountHelperMapper.java create mode 100644 src/main/java/trust/mapper/CountHelperMapper.xml create mode 100644 src/main/java/trust/mapper/RecordMapper.java create mode 100644 src/main/java/trust/mapper/RecordMapper.xml create mode 100644 src/main/java/trust/mapper/ServiceMapper.java create mode 100644 src/main/java/trust/mapper/ServiceMapper.xml create mode 100644 src/main/java/trust/mapper/UsageMapper.java create mode 100644 src/main/java/trust/mapper/UsageMapper.xml create mode 100644 src/main/java/trust/mapper/UserMapper.java create mode 100644 src/main/java/trust/mapper/UserMapper.xml create mode 100644 src/main/java/trust/role/ConsumerService.java create mode 100644 src/main/java/trust/role/PlatformService.java create mode 100644 src/main/java/trust/role/ProviderService.java create mode 100644 src/main/java/trust/role/Serve.java create mode 100644 src/main/java/trust/role/impl/ConsumerServiceImpl.java create mode 100644 src/main/java/trust/role/impl/PlatformServiceImpl.java create mode 100644 src/main/java/trust/role/impl/ProviderServiceImpl.java create mode 100644 src/main/java/trust/service/TestService.java create mode 100644 src/main/java/trust/service/impl/TestServiceImpl.java create mode 100644 src/main/resources/config.properties create mode 100644 src/main/resources/log4j.properties create mode 100644 src/main/resources/mybatis-config.xml create mode 100644 src/main/resources/spring-dataSource.xml create mode 100644 src/main/resources/spring-mvc.xml create mode 100644 src/main/resources/spring.xml create mode 100644 src/main/test/BasicGenerator.java create mode 100644 src/main/test/BasicGeneratorDemo.java create mode 100644 src/main/test/BasicOperation.java create mode 100644 src/main/test/CountedObject.java create mode 100644 src/main/test/DelegatingVehicleTracker.java create mode 100644 src/main/test/Ensemble.java create mode 100644 src/main/test/ExtendedOperation.java create mode 100644 src/main/test/Father.java create mode 100644 src/main/test/Generator.java create mode 100644 src/main/test/Herb.java create mode 100644 src/main/test/Item41/CollectionClassifier.java create mode 100644 src/main/test/Item41/Overriding.java create mode 100644 src/main/test/Item41/SetList.java create mode 100644 src/main/test/MonitorVehicleTracker.java create mode 100644 src/main/test/MutablePoint.java create mode 100644 src/main/test/Operation.java create mode 100644 src/main/test/Phase.java create mode 100644 src/main/test/PojoBug.java create mode 100644 src/main/test/Stack.java create mode 100644 src/main/test/Sub.java create mode 100644 src/main/test/Super.java create mode 100644 src/main/test/TestSum.java create mode 100644 src/main/test/Testsyn.java create mode 100644 src/main/test/ThreadA.java create mode 100644 src/main/test/aaa.java create mode 100644 src/main/test/concurrent/Beer.java create mode 100644 src/main/test/concurrent/LaunderThrowable.java create mode 100644 src/main/test/concurrent/PrimeProducer.java create mode 100644 src/main/test/concurrent/TestHarness.java create mode 100644 src/main/test/concurrent/TestIntterupt.java create mode 100644 src/main/test/sdadsad.java create mode 100644 src/main/test/test.java create mode 100644 src/main/test/testThread.java create mode 100644 src/main/webapp/WEB-INF/views/echarts.js create mode 100644 src/main/webapp/WEB-INF/views/macarons.js create mode 100644 src/main/webapp/WEB-INF/views/performanceList.jsp create mode 100644 src/main/webapp/WEB-INF/views/result.jsp create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/index.jsp create mode 100644 src/main/webapp/static/echarts.js create mode 100644 src/main/webapp/static/macarons.js diff --git a/TrustStudy.iml b/TrustStudy.iml new file mode 100644 index 0000000..fd382b8 --- /dev/null +++ b/TrustStudy.iml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3a7d77d --- /dev/null +++ b/pom.xml @@ -0,0 +1,410 @@ + + 4.0.0 + com.eliteams + TrustStudy + war + 1.0.0 + quick4j App + https://github.com/starzou/quick4j + + + TrustStudy + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${plugin.maven-compiler} + + ${project.build.jdk} + ${project.build.jdk} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${plugin.maven-surefire} + + ${skipTests} + + + + + + + + src/main/resources + + **/*.properties + **/*.xml + + true + + + src/main/java + + **/*.properties + **/*.xml + + true + + + + + + + UTF-8 + zh_CN + 1.7 + + + ${basedir}/src/test/resources/generatorConfig.xml + file:///${basedir}/src/test/resources/generatorConfig.properties + + + 1.3.1 + 3.1 + 2.18.1 + true + + + 4.11 + 4.2.4.RELEASE + 3.2.8 + 1.2.3 + 5.1.30 + 1.7.6 + 1.2.12 + 1.9.13 + 1.0.11 + 7.0.53 + 1.2 + 1.3.1 + 1.9 + 3.3 + 1.6.12 + 5.1.1.Final + + + + + org.codehaus.jackson + jackson-mapper-asl + 1.9.13 + + + + junit + junit + ${junit.version} + + + + + io.jsonwebtoken + jjwt + 0.7.0 + + + + + com.github.pagehelper + pagehelper + 5.0.0 + + + + org.apache.commons + commons-lang3 + 3.3.2 + + + + + + org.springframework + spring-core + ${spring.version} + + + + + + commons-dbcp + commons-dbcp + 1.2.2 + + + + javax.servlet + jstl + 1.2 + + + + taglibs + standard + 1.1.2 + + + + org.springframework + spring-web + ${spring.version} + + + + org.springframework + spring-jdbc + ${spring.version} + + + + org.springframework + spring-beans + ${spring.version} + + + + org.springframework + spring-context + ${spring.version} + + + + org.springframework + spring-webmvc + ${spring.version} + + + + + + + redis.clients + jedis + 2.9.0 + + + + + com.google.code.gson + gson + 2.8.0 + + + + + org.aspectj + aspectjweaver + 1.7.4 + + + + + + + org.springframework + spring-aop + ${spring.version} + + + + org.springframework + spring-context-support + ${spring.version} + + + + + + + org.mybatis + mybatis + ${mybatis.version} + + + + + + + mysql + mysql-connector-java + ${mysql.connector.version} + + + + + com.alibaba + druid + ${druid.version} + + + + + + + org.codehaus.jackson + jackson-mapper-asl + ${jackson.version} + + + + + log4j + log4j + ${log4j.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + + + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + commons-codec + commons-codec + ${commons.codec.version} + + + + commons-net + commons-net + ${commons.net.version} + + + + commons-logging + commons-logging + 1.1.3 + + + commons-collections + commons-collections + 3.2.1 + + + + + + + + + + + + org.hibernate + hibernate-validator + ${hibernate.validator.version} + + + + + + + org.mybatis + mybatis-spring + 1.2.3 + + + + redis.clients + jedis + 2.6.2 + jar + compile + + + org.springframework.data + spring-data-redis + 1.8.0.RELEASE + + + redis.clients + jedis + 2.9.0 + + + org.apache.commons + commons-pool2 + 2.0 + + + com.google.guava + guava + 14.0-rc2 + + + io.jsonwebtoken + jjwt + 0.7.0 + + + com.github.pagehelper + pagehelper + 5.0.0 + + + + com.alibaba + fastjson + 1.2.16 + + + + com.ning + async-http-client + 1.9.40 + + + + net.sf.kxml + kxml2 + 2.3.0 + + + xmlpull + xmlpull + 1.1.3.1 + + + + org.apache.httpcomponents + httpclient + 4.5.2 + + + org.apache.maven + maven-parent + 23 + pom + + + \ No newline at end of file diff --git a/src/main/java/trust/controller/TestController.java b/src/main/java/trust/controller/TestController.java new file mode 100644 index 0000000..a51de74 --- /dev/null +++ b/src/main/java/trust/controller/TestController.java @@ -0,0 +1,45 @@ +package trust.controller; + + +import com.google.gson.Gson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.servlet.ModelAndView; +import trust.entity.Result; +import trust.service.TestService; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.concurrent.BrokenBarrierException; + + +@Controller +@RequestMapping("/trust") +public class TestController { + + @Autowired + TestService testService; + + //开始进行模拟 + @RequestMapping(value="/test",method= RequestMethod.POST) + public void Test() throws BrokenBarrierException, InterruptedException { + testService.Run(); + } + + //获取实验的结果 + @RequestMapping(value="/result") + public ModelAndView Result(HttpServletRequest request) throws BrokenBarrierException, InterruptedException { + List results=testService.GetResult(); + Gson gson=new Gson(); + request.setAttribute("result", gson.toJson(results)); + return new ModelAndView("/result"); + } + + //清除当前测试的数据重新开始 + @RequestMapping(value="/clear",method= RequestMethod.POST) + public void Clear() { + testService.Clear(); + } +} diff --git a/src/main/java/trust/entity/CountHelper.java b/src/main/java/trust/entity/CountHelper.java new file mode 100644 index 0000000..46e0474 --- /dev/null +++ b/src/main/java/trust/entity/CountHelper.java @@ -0,0 +1,35 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/7/30. + * 用来显示结果的辅助类 + */ +public class CountHelper { + private int gen; + private int successCount; + private int totalCount; + + public int getTotalCount() { + return totalCount; + } + + public void setTotalCount(int totalCount) { + this.totalCount = totalCount; + } + + public int getSuccessCount() { + return successCount; + } + + public void setSuccessCount(int successCount) { + this.successCount = successCount; + } + + public int getGen() { + return gen; + } + + public void setGen(int gen) { + this.gen = gen; + } +} diff --git a/src/main/java/trust/entity/Record.java b/src/main/java/trust/entity/Record.java new file mode 100644 index 0000000..bac19db --- /dev/null +++ b/src/main/java/trust/entity/Record.java @@ -0,0 +1,55 @@ +package trust.entity; + +import java.util.Date; + +/** + * Created by inst1 on 2017/6/21. + * 可信反馈记录 + */ +public class Record { + private Integer id; + private Integer usr; + private Integer se; + private Date time; + private double trust; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getUsr() { + return usr; + } + + public void setUsr(Integer usr) { + this.usr = usr; + } + + public Integer getSe() { + return se; + } + + public void setSe(Integer se) { + this.se = se; + } + + public double getTrust() { + return trust; + } + + public void setTrust(double trust) { + this.trust = trust; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } +} diff --git a/src/main/java/trust/entity/Result.java b/src/main/java/trust/entity/Result.java new file mode 100644 index 0000000..c80c0ee --- /dev/null +++ b/src/main/java/trust/entity/Result.java @@ -0,0 +1,26 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/8/6. + * 用来存储统计后的结果 + */ +public class Result { + private int round; + private float trust; + + public int getRound() { + return round; + } + + public void setRound(int round) { + this.round = round; + } + + public float getTrust() { + return trust; + } + + public void setTrust(float trust) { + this.trust = trust; + } +} diff --git a/src/main/java/trust/entity/Ser.java b/src/main/java/trust/entity/Ser.java new file mode 100644 index 0000000..628da88 --- /dev/null +++ b/src/main/java/trust/entity/Ser.java @@ -0,0 +1,89 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/6/21. + * 服务提供商 + */ +public class Ser { + private int id; + private String name; + private int quality; + private int count; + private String role; + private float usability; + private float reliability; + private float responseTime; + private float throughPut; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getQuality() { + return quality; + } + + public void setQuality(int quality) { + this.quality = quality; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public String getRole() { + return role; + } + + public void setRole(String role) { + this.role = role; + } + + public float getUsability() { + return usability; + } + + public void setUsability(float usability) { + this.usability = usability; + } + + public float getReliability() { + return reliability; + } + + public void setReliability(float reliability) { + this.reliability = reliability; + } + + public float getResponseTime() { + return responseTime; + } + + public void setResponseTime(float responseTime) { + this.responseTime = responseTime; + } + + public float getThroughPut() { + return throughPut; + } + + public void setThroughPut(float throughPut) { + this.throughPut = throughPut; + } +} diff --git a/src/main/java/trust/entity/TrustMeasureCustom.java b/src/main/java/trust/entity/TrustMeasureCustom.java new file mode 100644 index 0000000..ebcc63c --- /dev/null +++ b/src/main/java/trust/entity/TrustMeasureCustom.java @@ -0,0 +1,25 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/11/9. + */ +public class TrustMeasureCustom { + private double trust; + private double weight; + + public double getTrust() { + return trust; + } + + public void setTrust(double trust) { + this.trust = trust; + } + + public double getWeight() { + return weight; + } + + public void setWeight(double weight) { + this.weight = weight; + } +} diff --git a/src/main/java/trust/entity/Usage.java b/src/main/java/trust/entity/Usage.java new file mode 100644 index 0000000..93c6a27 --- /dev/null +++ b/src/main/java/trust/entity/Usage.java @@ -0,0 +1,34 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/11/6. + */ +public class Usage { + private int id; + private int usr; + private int se; + + public int getUsr() { + return usr; + } + + public void setUsr(int usr) { + this.usr = usr; + } + + public int getSe() { + return se; + } + + public void setSe(int se) { + this.se = se; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } +} diff --git a/src/main/java/trust/entity/User.java b/src/main/java/trust/entity/User.java new file mode 100644 index 0000000..0cf5030 --- /dev/null +++ b/src/main/java/trust/entity/User.java @@ -0,0 +1,71 @@ +package trust.entity; + +/** + * Created by inst1 on 2017/6/21. + * 用户 + */ +public class User { + private int id; + private String name; + private int count; + private float usability; + private float reliability; + private float responseTime; + private float throughPut; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public float getUsability() { + return usability; + } + + public void setUsability(float usability) { + this.usability = usability; + } + + public float getReliability() { + return reliability; + } + + public void setReliability(float reliability) { + this.reliability = reliability; + } + + public float getResponseTime() { + return responseTime; + } + + public void setResponseTime(float responseTime) { + this.responseTime = responseTime; + } + + public float getThroughPut() { + return throughPut; + } + + public void setThroughPut(float throughPut) { + this.throughPut = throughPut; + } +} diff --git a/src/main/java/trust/mapper/CountHelperMapper.java b/src/main/java/trust/mapper/CountHelperMapper.java new file mode 100644 index 0000000..e378ba4 --- /dev/null +++ b/src/main/java/trust/mapper/CountHelperMapper.java @@ -0,0 +1,14 @@ +package trust.mapper; + +import trust.entity.CountHelper; + +import java.util.List; + +/** + * Created by inst1 on 2017/6/21. + */ +public interface CountHelperMapper { + int insert(CountHelper countHelper); + List selectAll(); + +} diff --git a/src/main/java/trust/mapper/CountHelperMapper.xml b/src/main/java/trust/mapper/CountHelperMapper.xml new file mode 100644 index 0000000..9f06b3d --- /dev/null +++ b/src/main/java/trust/mapper/CountHelperMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + gen,success_count,total_count + + + insert into count_helper (gen,success_count,total_count) + values ( #{gen,jdbcType=INTEGER}, #{successCount,jdbcType=INTEGER}, #{totalCount,jdbcType=INTEGER}) + + + + + + \ No newline at end of file diff --git a/src/main/java/trust/mapper/RecordMapper.java b/src/main/java/trust/mapper/RecordMapper.java new file mode 100644 index 0000000..687625b --- /dev/null +++ b/src/main/java/trust/mapper/RecordMapper.java @@ -0,0 +1,16 @@ +package trust.mapper; + +import trust.entity.Record; + +import java.util.List; + +/** + * Created by inst1 on 2017/6/21. + */ +public interface RecordMapper { + int insert(Record r); + int clearAll(); + List selectByService(Integer se); + int clearPart(); + List selectByUserAndService(Integer usr,Integer se); +} diff --git a/src/main/java/trust/mapper/RecordMapper.xml b/src/main/java/trust/mapper/RecordMapper.xml new file mode 100644 index 0000000..0f4743c --- /dev/null +++ b/src/main/java/trust/mapper/RecordMapper.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + id, usr, se,time, trust + + + insert into record (usr, se,time,trust) + values ( #{usr,jdbcType=INTEGER}, #{se,jdbcType=INTEGER}, #{time,jdbcType=TIMESTAMP}, #{trust,jdbcType=DOUBLE}) + + + + + + + + delete from record; + update Service set count=0 and counta=0; + update user set count=0; + delete from count_helper; + delete from useage; + + + delete from record; + update Service set count=0 and counta=0; + update user set count=0; + delete from useage; + + + \ No newline at end of file diff --git a/src/main/java/trust/mapper/ServiceMapper.java b/src/main/java/trust/mapper/ServiceMapper.java new file mode 100644 index 0000000..084b1e9 --- /dev/null +++ b/src/main/java/trust/mapper/ServiceMapper.java @@ -0,0 +1,14 @@ +package trust.mapper; + +import trust.entity.Ser; + +import java.util.List; + +/** + * Created by inst1 on 2017/6/21. + */ +public interface ServiceMapper { + List selectByQuality(Integer low, Integer high); + int count(Integer id); + int countA(Integer id); +} diff --git a/src/main/java/trust/mapper/ServiceMapper.xml b/src/main/java/trust/mapper/ServiceMapper.xml new file mode 100644 index 0000000..f824f5a --- /dev/null +++ b/src/main/java/trust/mapper/ServiceMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + id, name, quality,count,role,usability,reliability,response_time,through_put + + + + + update service set count=count+1 where id=#{id} + + + + update service set counta=counta+1 where id=#{id} + + + + + \ No newline at end of file diff --git a/src/main/java/trust/mapper/UsageMapper.java b/src/main/java/trust/mapper/UsageMapper.java new file mode 100644 index 0000000..9fde606 --- /dev/null +++ b/src/main/java/trust/mapper/UsageMapper.java @@ -0,0 +1,13 @@ +package trust.mapper; + +import trust.entity.Usage; + +import java.util.List; + +/** + * Created by inst1 on 2017/11/6. + */ +public interface UsageMapper { + int insertUsage(Usage usage); + List selectUserUsage(int se); +} diff --git a/src/main/java/trust/mapper/UsageMapper.xml b/src/main/java/trust/mapper/UsageMapper.xml new file mode 100644 index 0000000..1cdc47d --- /dev/null +++ b/src/main/java/trust/mapper/UsageMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + id, usr, se; + + + + + + + insert useage (usr,se) + values(#{usr},#{se}) + + + \ No newline at end of file diff --git a/src/main/java/trust/mapper/UserMapper.java b/src/main/java/trust/mapper/UserMapper.java new file mode 100644 index 0000000..3807e3c --- /dev/null +++ b/src/main/java/trust/mapper/UserMapper.java @@ -0,0 +1,14 @@ +package trust.mapper; + +import trust.entity.User; + +import java.util.List; + +/** + * Created by inst1 on 2017/6/21. + */ +public interface UserMapper { + List selectAll(); + int count(Integer id); + int countSuccess(); +} diff --git a/src/main/java/trust/mapper/UserMapper.xml b/src/main/java/trust/mapper/UserMapper.xml new file mode 100644 index 0000000..b2154fc --- /dev/null +++ b/src/main/java/trust/mapper/UserMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + id, name, count,usability,reliability,response_time,through_put + + + + + + + update user set count=count+1 where id=#{id} + + + \ No newline at end of file diff --git a/src/main/java/trust/role/ConsumerService.java b/src/main/java/trust/role/ConsumerService.java new file mode 100644 index 0000000..748dd26 --- /dev/null +++ b/src/main/java/trust/role/ConsumerService.java @@ -0,0 +1,13 @@ +package trust.role; + +import trust.entity.Ser; +import trust.entity.User; + +import java.util.List; + +/** + * Created by inst1 on 2017/7/16. + */ +public interface ConsumerService { + void UseService(List services , User user, int gen); +} diff --git a/src/main/java/trust/role/PlatformService.java b/src/main/java/trust/role/PlatformService.java new file mode 100644 index 0000000..7b5178d --- /dev/null +++ b/src/main/java/trust/role/PlatformService.java @@ -0,0 +1,13 @@ +package trust.role; + +import trust.entity.Ser; +import trust.entity.User; + +import java.util.List; + +/** + * Created by inst1 on 2017/10/31. + */ +public interface PlatformService { + List getTrustedService(int ranLow, int ranHigh, User user,int choice); +} diff --git a/src/main/java/trust/role/ProviderService.java b/src/main/java/trust/role/ProviderService.java new file mode 100644 index 0000000..8142094 --- /dev/null +++ b/src/main/java/trust/role/ProviderService.java @@ -0,0 +1,10 @@ +package trust.role; + +import trust.entity.Ser; + +/** + * Created by inst1 on 2017/7/16. + */ +public interface ProviderService { + Ser ProduceServiceActualQuality(Ser se,int order); +} diff --git a/src/main/java/trust/role/Serve.java b/src/main/java/trust/role/Serve.java new file mode 100644 index 0000000..f743eda --- /dev/null +++ b/src/main/java/trust/role/Serve.java @@ -0,0 +1,78 @@ +package trust.role; + +import trust.entity.Ser; +import trust.entity.User; + +import java.util.List; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by inst1 on 2017/6/21. + */ + + +public class Serve extends Thread { + int gen; + AtomicInteger count; + User user; + PlatformService platformService; + ConsumerService consumerService; + ProviderService providerService; + CountDownLatch countDownLatch; + CyclicBarrier beforeBarrier; + CyclicBarrier afterBarrier; + int choice; + + //初始化多线程,将需要用到的mapper和service加入到类中 + public Serve(User user, PlatformService platformService, ConsumerService consumerService, ProviderService providerService, CountDownLatch countDownLatch, AtomicInteger count, CyclicBarrier beforeBarrier, CyclicBarrier afterBarrie, int choice,int gen){ + //将客户端需要的东西注入到线程中,由于mapper等需要连接的东西是通过threadlocal存储的所以不存在线程安全问题 + this.user=user; + this.platformService=platformService; + this.consumerService=consumerService; + this.providerService=providerService; + this.countDownLatch=countDownLatch; + this.count=count; + this.beforeBarrier=beforeBarrier; + this.afterBarrier=afterBarrie; + this.choice=choice; + this.gen=gen; + } + + @Override + public void run() { + //客户端初始化完,等待其他用户的客户端 + countDownLatch.countDown(); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + //定义代,根据代别来统计准确率 + for(int i=0;i services=platformService.getTrustedService(ranLow,ranHigh,user,choice); + consumerService.UseService(services,user,i); + + + + } + try { + //根据每个迭代的统计操作控制 + beforeBarrier.await(); + afterBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/trust/role/impl/ConsumerServiceImpl.java b/src/main/java/trust/role/impl/ConsumerServiceImpl.java new file mode 100644 index 0000000..02e2147 --- /dev/null +++ b/src/main/java/trust/role/impl/ConsumerServiceImpl.java @@ -0,0 +1,80 @@ +package trust.role.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import trust.entity.Record; +import trust.entity.Ser; +import trust.entity.Usage; +import trust.entity.User; +import trust.mapper.RecordMapper; +import trust.mapper.ServiceMapper; +import trust.mapper.UsageMapper; +import trust.mapper.UserMapper; +import trust.role.ConsumerService; +import trust.role.ProviderService; + +import java.util.Date; +import java.util.List; +import java.util.Random; + +/** + * Created by inst1 on 2017/7/16. + */ +@Service("ConsumerService") +public class ConsumerServiceImpl implements ConsumerService { + @Autowired + private ProviderService providerService; + + @Autowired + private UserMapper userMapper; + + @Autowired + private ServiceMapper serviceMapper; + + @Autowired + private RecordMapper recordMapper; + + @Autowired + private UsageMapper usageMapper; + + private int SelectService(List services){ + //根据被评估为可信的服务列表随机选择服务 + Random random=new Random(); + return random.nextInt(services.size()); + } + @Override + public void UseService(List services ,User user,int gen){ + int len=services.size(); + //如果有可选的服务则选择服务 + if(len>0){ + int ran =SelectService(services); + Record r = new Record(); + //调用服务器service模拟服务提供过程获取实际qos值 + Ser actual=providerService.ProduceServiceActualQuality(services.get(ran),gen); + //计算可信值 + float trust=actual.getReliability()*user.getReliability()+actual.getThroughPut()*user.getThroughPut()+actual.getResponseTime()*user.getResponseTime()+actual.getUsability()*user.getUsability(); + //录入反馈记录 + r.setSe(services.get(ran).getId()); + r.setTime(new Date()); + r.setUsr(user.getId()); + r.setTrust(trust ); + //如果服务最终评估为实际可信,则证明推荐和评估过程成功了。 + if(trust>=0.9){ + userMapper.count(user.getId()); + } + + //插入反馈记录 + recordMapper.insert(r); + //记录服务被调用次数 + serviceMapper.count(services.get(ran).getId()); + //记录用户调用的服务 + Usage usage=new Usage(); + usage.setSe(services.get(ran).getId()); + usage.setUsr(user.getId()); + usageMapper.insertUsage(usage); + } + } + + + +} diff --git a/src/main/java/trust/role/impl/PlatformServiceImpl.java b/src/main/java/trust/role/impl/PlatformServiceImpl.java new file mode 100644 index 0000000..52eb8e2 --- /dev/null +++ b/src/main/java/trust/role/impl/PlatformServiceImpl.java @@ -0,0 +1,123 @@ +package trust.role.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import trust.entity.Record; +import trust.entity.Ser; +import trust.entity.TrustMeasureCustom; +import trust.entity.User; +import trust.mapper.RecordMapper; +import trust.mapper.ServiceMapper; +import trust.mapper.UsageMapper; +import trust.role.PlatformService; + +import java.util.List; + +/** + * Created by inst1 on 2017/10/31. + */ + + +@Service("PlatformService") +public class PlatformServiceImpl implements PlatformService { + @Autowired + private ServiceMapper serviceMapper; + + @Autowired + private RecordMapper recordMapper; + + @Autowired + private UsageMapper usageMapper; + + @Override + public List getTrustedService(int ranLow, int ranHigh, User user,int choice) { + + //根据qos需求初步筛选服务 + List services = serviceMapper.selectByQuality(ranLow, ranHigh); + Integer len = services.size(); + //评估每个服务的可信性 + for (int i = 0; i < len; i++) { + //记录服务曾被选中次数,便于可能的统计工作 + serviceMapper.countA(services.get(i).getId()); + //获取所有使用过该服务的历史评估信息 + double avg=measureServiceTrust(services.get(i),choice); + + //根据平均值选择是否不考虑该服务 + if (avg < 0.9 && services.get(i).getCount() > 4) { + //不选择则去除该服务,方便最后用户随机抽取可信服务 + services.remove(i); + i--; + len--; + + } + } + return services; + } + + private TrustMeasureCustom getAverageByTime(List records){ + Integer reLen = records.size(); + //获取当前时间 + long now =System.currentTimeMillis(); + double avg = 0; + double timeWeightAll = 0; + double timeWeight = 0; + for (int y = 0; y < reLen; y++) { + //获取每个历史纪录到当前时间的时间差,用来计算比重 + long between=(now-records.get(y).getTime().getTime())/1000; + //计算比重值,乘上信任值加到总计中,同时加到总比重中,最后一除就是可信值 + timeWeight=Math.pow(2.5,-between); + timeWeightAll+=timeWeight; + avg+=records.get(y).getTrust()*timeWeight; + } + //计算平均评分 + + + TrustMeasureCustom trustMeasureCustom=new TrustMeasureCustom(); + trustMeasureCustom.setTrust(avg); + trustMeasureCustom.setWeight(timeWeightAll); + return trustMeasureCustom; + } + + private TrustMeasureCustom getAverage(List records){ + Integer reLen = records.size(); + //获取当前时间 + double avg = 0; + for (int y = 0; y < reLen; y++) { + //在无时间衰减记录下,直接取平均就行 + avg += records.get(y).getTrust(); + } + + + //计算平均评分 + + TrustMeasureCustom trustMeasureCustom=new TrustMeasureCustom(); + trustMeasureCustom.setTrust(avg); + trustMeasureCustom.setWeight(reLen); + return trustMeasureCustom; + } + + double measureServiceTrust(Ser ser,int choice){ + List users=usageMapper.selectUserUsage(ser.getId()); + double sum=0,weight=0; + for(int i=0;i records=recordMapper.selectByUserAndService(user,ser); + if(choice==0){ + return getAverageByTime(records); + } + else{ + return getAverage(records); + } + + } + + + +} diff --git a/src/main/java/trust/role/impl/ProviderServiceImpl.java b/src/main/java/trust/role/impl/ProviderServiceImpl.java new file mode 100644 index 0000000..60432f4 --- /dev/null +++ b/src/main/java/trust/role/impl/ProviderServiceImpl.java @@ -0,0 +1,32 @@ +package trust.role.impl; + +import org.springframework.stereotype.Service; +import trust.entity.Ser; +import trust.role.ProviderService; + +import java.util.Random; + +/** + * Created by inst1 on 2017/7/16. + */ +@Service("ProviderService") +public class ProviderServiceImpl implements ProviderService { + public Ser ProduceServiceActualQuality(Ser ser,int round){ + Random random=new Random(); + //模拟不良商家为了获取用户信任而使用的开始正常服务后来低水平服务的不可信行为,此模拟可凸显出不使用时间衰减因子时造成的可信错误评价情况 + if(round<3||ser.getRole().equals("good")){ + ser.setUsability((float) (random.nextFloat()/10+0.9)); + ser.setReliability((float) (random.nextFloat()/10+0.9)); + ser.setResponseTime((float) (random.nextFloat()/10+0.9)); + ser.setThroughPut((float) (random.nextFloat()/10+0.9)); + } + else{ + ser.setUsability((float) (random.nextFloat()/4+0.7)); + ser.setReliability((float) (random.nextFloat()/4+0.7)); + ser.setResponseTime((float) (random.nextFloat()/4+0.7)); + ser.setThroughPut((float) (random.nextFloat()/4+0.7)); + } + + return ser; + } +} diff --git a/src/main/java/trust/service/TestService.java b/src/main/java/trust/service/TestService.java new file mode 100644 index 0000000..6013baf --- /dev/null +++ b/src/main/java/trust/service/TestService.java @@ -0,0 +1,16 @@ +package trust.service; + +import trust.entity.Result; + +import java.util.List; +import java.util.concurrent.BrokenBarrierException; + +/** + * Created by inst1 on 2017/6/21. + */ +public interface TestService { + void Clear(); + List GetResult(); + void Run() throws BrokenBarrierException, InterruptedException; + +} diff --git a/src/main/java/trust/service/impl/TestServiceImpl.java b/src/main/java/trust/service/impl/TestServiceImpl.java new file mode 100644 index 0000000..f3785d4 --- /dev/null +++ b/src/main/java/trust/service/impl/TestServiceImpl.java @@ -0,0 +1,135 @@ +package trust.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import trust.entity.CountHelper; +import trust.entity.Result; +import trust.entity.User; +import trust.mapper.CountHelperMapper; +import trust.mapper.RecordMapper; +import trust.mapper.ServiceMapper; +import trust.mapper.UserMapper; +import trust.role.ConsumerService; +import trust.role.PlatformService; +import trust.role.ProviderService; +import trust.role.Serve; +import trust.service.TestService; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by inst1 on 2017/6/21. + */ +@Service("TestService") +public class TestServiceImpl implements TestService { + public volatile AtomicInteger k=new AtomicInteger(); + @Autowired + UserMapper userMapper; + + @Autowired + ServiceMapper serviceMapper; + + @Autowired + RecordMapper recordMapper; + + @Autowired + ConsumerService consumerService; + + @Autowired + ProviderService providerService; + + @Autowired + CountHelperMapper countHelperMapper; + @Autowired + PlatformService platformService; + private Integer id; + private void Test(int gen,int choice) throws BrokenBarrierException, InterruptedException { + //从数据库中取出用户 + final List users=userMapper.selectAll(); + long start=System.currentTimeMillis(); + //记录总共调用服务次数的数值,为之后统计图服务。 + AtomicInteger test=new AtomicInteger(0); + //第一个栅栏用来拦住全部正在调用服务的用户,让服务统计。确保全部用户完成了调用量再统计 + CyclicBarrier beforeBarrier=new CyclicBarrier(users.size()+1); + //第二个栅栏用来确保在统计时用户不会在继续调用,等到统计完成了再放用户进行下一轮使用,确保一致性。 + CyclicBarrier afterBarrier=new CyclicBarrier(users.size()+1); + //确保全部用户线程同时开启(可有可无) + final CountDownLatch countDownLatch=new CountDownLatch(users.size()); + //创建客户端,开始调用服务并模拟平台自动评估服务可信值,模拟用户在可选列表的服务列表中随机选择服务,服务提供商模拟提供特定范围内的服务质量,根据对比值自动产生反馈值并录入数据库中,将上面步骤中加入的栅栏加入到每个客户端中协助控制 + for(User user:users){ + Serve s=new Serve(user,platformService,consumerService,providerService,countDownLatch,test,beforeBarrier,afterBarrier,choice,gen); + s.start(); + } + //对应多个代用来在最终统计图中显示出平台在渐渐加强服务评估的精度,每次使用第一个栅栏等待所有的用户调用完当前迭代的量,第二个栅栏卡住用户等自己统计完插入进数据库再放行 + for(int i=0;i GetResult() { + //获取全部迭代中的数据 + List countHelpers=countHelperMapper.selectAll(); + List results=new ArrayList<>(); + int j=countHelpers.size(); + for(int i=0;i + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring-dataSource.xml b/src/main/resources/spring-dataSource.xml new file mode 100644 index 0000000..f07528d --- /dev/null +++ b/src/main/resources/spring-dataSource.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring-mvc.xml b/src/main/resources/spring-mvc.xml new file mode 100644 index 0000000..9e8b761 --- /dev/null +++ b/src/main/resources/spring-mvc.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/spring.xml b/src/main/resources/spring.xml new file mode 100644 index 0000000..f751119 --- /dev/null +++ b/src/main/resources/spring.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/test/BasicGenerator.java b/src/main/test/BasicGenerator.java new file mode 100644 index 0000000..25e9ea3 --- /dev/null +++ b/src/main/test/BasicGenerator.java @@ -0,0 +1,20 @@ +//: net/mindview/util/BasicGenerator.java +// Automatically create a Generator, given a class +// with a default (no-arg) constructor. + +public class BasicGenerator implements Generator { + private Class type; + public BasicGenerator(Class type){ this.type = type; } + public T next() { + try { + // Assumes type is a public class: + return type.newInstance(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + // Produce a Default generator given a type token: + public static Generator create(Class type) { + return new BasicGenerator(type); + } +} ///:~ diff --git a/src/main/test/BasicGeneratorDemo.java b/src/main/test/BasicGeneratorDemo.java new file mode 100644 index 0000000..9384818 --- /dev/null +++ b/src/main/test/BasicGeneratorDemo.java @@ -0,0 +1,17 @@ +//: generics/BasicGeneratorDemo.java + + +public class BasicGeneratorDemo { + public static void main(String[] args) { + Generator gen = + BasicGenerator.create(CountedObject.class); + for(int i = 0; i < 5; i++) + System.out.println(gen.next()); + } +} /* Output: +CountedObject 0 +CountedObject 1 +CountedObject 2 +CountedObject 3 +CountedObject 4 +*///:~ diff --git a/src/main/test/BasicOperation.java b/src/main/test/BasicOperation.java new file mode 100644 index 0000000..db5db66 --- /dev/null +++ b/src/main/test/BasicOperation.java @@ -0,0 +1,22 @@ +// Emulated extensible enum using an interface - Basic implementation - Page 165 +public enum BasicOperation implements Operation { + PLUS("+") { + public double apply(double x, double y) { return x + y; } + }, + MINUS("-") { + public double apply(double x, double y) { return x - y; } + }, + TIMES("*") { + public double apply(double x, double y) { return x * y; } + }, + DIVIDE("/") { + public double apply(double x, double y) { return x / y; } + }; + private final String symbol; + BasicOperation(String symbol) { + this.symbol = symbol; + } + @Override public String toString() { + return symbol; + } +} diff --git a/src/main/test/CountedObject.java b/src/main/test/CountedObject.java new file mode 100644 index 0000000..757f2ae --- /dev/null +++ b/src/main/test/CountedObject.java @@ -0,0 +1,8 @@ +//: generics/CountedObject.java + +public class CountedObject { + private static long counter = 0; + private final long id = counter++; + public long id() { return id; } + public String toString() { return "CountedObject " + counter;} +} ///:~ diff --git a/src/main/test/DelegatingVehicleTracker.java b/src/main/test/DelegatingVehicleTracker.java new file mode 100644 index 0000000..e9ecaed --- /dev/null +++ b/src/main/test/DelegatingVehicleTracker.java @@ -0,0 +1,53 @@ +import java.awt.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * DelegatingVehicleTracker + *

+ * Delegating thread safety to a ConcurrentHashMap + * + * @author Brian Goetz and Tim Peierls + */ +public class DelegatingVehicleTracker { + private final ConcurrentMap locations; + private final Map unmodifiableMap; + + public DelegatingVehicleTracker(Map points) { + locations = new ConcurrentHashMap(points); + unmodifiableMap = Collections.unmodifiableMap(locations); + } + + public Map getLocations() { + return unmodifiableMap; + } + + public Point getLocation(String id) { + return locations.get(id); + } + + public void setLocation(String id, int x, int y) { + locations.put(id, new Point(x, y)); + } + Vector sada=new Vector<>(); + + // Alternate version of getLocations (Listing 4.8) + public Map getLocationsAsStatic() { + + return Collections.unmodifiableMap( + new HashMap(locations)); + } + + public static void main(String args[]){ + Map points=new HashMap<>(); + DelegatingVehicleTracker delegatingVehicleTracker=new DelegatingVehicleTracker(points); + Map point1=delegatingVehicleTracker.getLocations(); + delegatingVehicleTracker.setLocation("asdasda",123,123); + System.out.println(delegatingVehicleTracker.getLocations().get("asdasda")); + } +} + diff --git a/src/main/test/Ensemble.java b/src/main/test/Ensemble.java new file mode 100644 index 0000000..9a3be77 --- /dev/null +++ b/src/main/test/Ensemble.java @@ -0,0 +1,16 @@ +// Enum with integer data stored in an instance field +public enum Ensemble { + SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5), + SEXTET(6), SEPTET(7), OCTET(8), DOUBLE_QUARTET(8), + NONET(9), DECTET(10), TRIPLE_QUARTET(12); + + private final int numberOfMusicians; + Ensemble(int size) { this.numberOfMusicians = size; } + public int numberOfMusicians() { return ordinal(); } + public static void main(String args[]){ + Ensemble ensemble=Ensemble.OCTET; + + System.out.println(ensemble.ordinal()); + + } +} diff --git a/src/main/test/ExtendedOperation.java b/src/main/test/ExtendedOperation.java new file mode 100644 index 0000000..45e192d --- /dev/null +++ b/src/main/test/ExtendedOperation.java @@ -0,0 +1,50 @@ +// Emulated extension enum - Page 166-167 + +import java.util.*; + +public enum ExtendedOperation implements Operation { + EXP("^") { + public double apply(double x, double y) { + return Math.pow(x, y); + } + }, + REMAINDER("%") { + public double apply(double x, double y) { + return x % y; + } + }; + + private final String symbol; + ExtendedOperation(String symbol) { + this.symbol = symbol; + } + @Override public String toString() { + return symbol; + } + + // Test class to exercise all operations in "extension enum" - Page 167 + public static void main(String[] args) { + double x = Double.parseDouble(String.valueOf(23)); + double y = Double.parseDouble(String.valueOf(32)); + test(ExtendedOperation.class, x, y); + + System.out.println(); // Print a blank line between tests + test2(Arrays.asList(ExtendedOperation.values()), x, y); + } + + // test parameter is a bounded type token (Item 29) + private static & Operation> void test( + Class opSet, double x, double y) { + for (Operation op : opSet.getEnumConstants()) + System.out.printf("%f %s %f = %f%n", + x, op, y, op.apply(x, y)); + } + + // test parameter is a bounded wildcard type (Item 28) + private static void test2(Collection opSet, + double x, double y) { + for (Operation op : opSet) + System.out.printf("%f %s %f = %f%n", + x, op, y, op.apply(x, y)); + } +} diff --git a/src/main/test/Father.java b/src/main/test/Father.java new file mode 100644 index 0000000..37f7aed --- /dev/null +++ b/src/main/test/Father.java @@ -0,0 +1,37 @@ +/** + * Created by inst1 on 2017/7/7. + */ +public class Father { + + + static int length; + static int height; + static int[][] arr1 = {{1,2,8,9},{2,4,9,12},{4,7,10,13},{6,8,11,15}}; + public static boolean Find(int target, int[][] array) { + height=array[0].length; + length=array.length; + + return cut(target,length-1,0,array); + } + public static boolean cut(int target, int left, int down, int[][] array){ + + if(left<0||down==height){ + return false; + } + + if(array[left][down]==target){ + return true; + } + else if(array[left][down]>target){ + return cut(target,--left,down,array); + } + else { + return cut(target,left,++down,array); + } + + + } + public static void main(String args[]){ + System.out.print(Find(7,arr1)); + + }} \ No newline at end of file diff --git a/src/main/test/Generator.java b/src/main/test/Generator.java new file mode 100644 index 0000000..d106de6 --- /dev/null +++ b/src/main/test/Generator.java @@ -0,0 +1,4 @@ +//: net/mindview/util/Generator.java +// A generic interface. + +public interface Generator { T next(); } ///:~ diff --git a/src/main/test/Herb.java b/src/main/test/Herb.java new file mode 100644 index 0000000..c8d1ce3 --- /dev/null +++ b/src/main/test/Herb.java @@ -0,0 +1,40 @@ +// Using an EnumMap to associate data with an enum - Pages 161-162 + +import java.util.*; + +// Simplistic class representing a culinary herb - Page 161 +public class Herb { + public enum Type { ANNUAL, PERENNIAL, BIENNIAL } + + private final String name; + private final Type type; + + Herb(String name, Type type) { + this.name = name; + this.type = type; + } + + @Override public String toString() { + return name; + } + + public static void main(String[] args) { + Herb[] garden = { + new Herb("Basil", Type.ANNUAL), + new Herb("Carroway", Type.BIENNIAL), + new Herb("Dill", Type.ANNUAL), + new Herb("Lavendar", Type.PERENNIAL), + new Herb("Parsley", Type.BIENNIAL), + new Herb("Rosemary", Type.PERENNIAL) + }; + + // Using an EnumMap to associate data with an enum - Page 162 + Map> herbsByType = + new EnumMap>(Type.class); + for (Type t : Type.values()) + herbsByType.put(t, new HashSet()); + for (Herb h : garden) + herbsByType.get(h.type).add(h); + System.out.println(herbsByType); + } +} diff --git a/src/main/test/Item41/CollectionClassifier.java b/src/main/test/Item41/CollectionClassifier.java new file mode 100644 index 0000000..12bd152 --- /dev/null +++ b/src/main/test/Item41/CollectionClassifier.java @@ -0,0 +1,29 @@ +package Item41;// Broken! - What does this program print? + +import java.util.*; +import java.math.*; + +public class CollectionClassifier { + public static String classify(Set s) { + return "Set"; + } + + public static String classify(List lst) { + return "List"; + } + + public static String classify(Collection c) { + return "Unknown Collection"; + } + + public static void main(String[] args) { + Collection[] collections = { + new HashSet(), + new ArrayList(), + new HashMap().values() + }; + + for (Collection c : collections) + System.out.println(classify(c)); + } +} diff --git a/src/main/test/Item41/Overriding.java b/src/main/test/Item41/Overriding.java new file mode 100644 index 0000000..bddd43d --- /dev/null +++ b/src/main/test/Item41/Overriding.java @@ -0,0 +1,23 @@ +package Item41;// Overriding demonstration - Page 192 + +class Wine { + String name() { return "wine"; } +} + +class SparklingWine extends Wine { + @Override String name() { return "sparkling wine"; } +} + +class Champagne extends SparklingWine { + @Override String name() { return "champagne"; } +} + +public class Overriding { + public static void main(String[] args) { + Wine[] wines = { + new Wine(), new SparklingWine(), new Champagne() + }; + for (Wine wine : wines) + System.out.println(wine.name()); + } +} diff --git a/src/main/test/Item41/SetList.java b/src/main/test/Item41/SetList.java new file mode 100644 index 0000000..5d1f52a --- /dev/null +++ b/src/main/test/Item41/SetList.java @@ -0,0 +1,23 @@ +package Item41;// What does this program print? - Page 194 + +import java.util.*; + +public class SetList { + public static void main(String[] args) { + Set set = new TreeSet(); + List list = new ArrayList(); + + for (int i = -3; i < 3; i++) { + set.add(i); + list.add(i); + } + + for (int i = 0; i < 3; i++) { + list.remove(i); + set.remove(i); + + } + + System.out.println(set + " " + list); + } +} diff --git a/src/main/test/MonitorVehicleTracker.java b/src/main/test/MonitorVehicleTracker.java new file mode 100644 index 0000000..020c4de --- /dev/null +++ b/src/main/test/MonitorVehicleTracker.java @@ -0,0 +1,61 @@ +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * MonitorVehicleTracker + *

+ * Monitor-based vehicle tracker implementation + * + * @author Brian Goetz and Tim Peierls + */ + public class MonitorVehicleTracker { + private final Map locations; + + public MonitorVehicleTracker(Map locations) { + this.locations =deepCopy(locations); + } + + public synchronized Map getLocations() { + return locations; + } + + public synchronized MutablePoint getLocation(String id) { + MutablePoint loc = locations.get(id); + return loc == null ? null : new MutablePoint(loc); + } + + public synchronized void setLocation(String id, int x, int y) { + MutablePoint loc = locations.get(id); + if (loc == null) + throw new IllegalArgumentException("No such ID: " + id); + loc.x = x; + loc.y = y; + } + + private static Map deepCopy(Map m) { + Map result = new HashMap(); + + for (String id : m.keySet()) + result.put(id, new MutablePoint(m.get(id))); + + return Collections.unmodifiableMap(result); + } + + public static void main(String qrgs[]){ + Map locations=new HashMap(); + + MonitorVehicleTracker monitorVehicleTracker=new MonitorVehicleTracker(locations); + Map locations1=monitorVehicleTracker.getLocations(); + System.out.println(locations==locations1); + + locations1=monitorVehicleTracker.getLocations(); + locations1.put("test",new MutablePoint()); + locations1=monitorVehicleTracker.getLocations(); + System.out.println(locations1); + AtomicLong as=new AtomicLong(213213); + + } +} + diff --git a/src/main/test/MutablePoint.java b/src/main/test/MutablePoint.java new file mode 100644 index 0000000..751c22b --- /dev/null +++ b/src/main/test/MutablePoint.java @@ -0,0 +1,20 @@ +/** + * MutablePoint + *

+ * Mutable Point class similar to java.awt.Point + * + * @author Brian Goetz and Tim Peierls + */ +public class MutablePoint { + public int x, y; + + public MutablePoint() { + x = 0; + y = 0; + } + + public MutablePoint(MutablePoint p) { + this.x = p.x; + this.y = p.y; + } +} diff --git a/src/main/test/Operation.java b/src/main/test/Operation.java new file mode 100644 index 0000000..a8ffdbf --- /dev/null +++ b/src/main/test/Operation.java @@ -0,0 +1,4 @@ +// Emulated extensible enum using an interface - Page 165 +public interface Operation { + double apply(double x, double y); +} diff --git a/src/main/test/Phase.java b/src/main/test/Phase.java new file mode 100644 index 0000000..f86c2b2 --- /dev/null +++ b/src/main/test/Phase.java @@ -0,0 +1,48 @@ +// Using a nested EnumMap to associate data with enum pairs - Pags 163-164 + +import java.util.*; + +public enum Phase { + SOLID, LIQUID, GAS; + + public enum Transition { + MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID), + BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID), + SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID); + + private final Phase src; + private final Phase dst; + + Transition(Phase src, Phase dst) { + this.src = src; + this.dst = dst; + } + // Initialize the phase transition map + private static final Map> m = + new EnumMap>(Phase.class); + static { + for (Phase p : Phase.values()) + m.put(p,new EnumMap(Phase.class)); + for (Transition trans : Transition.values()) { + m.get(trans.src).put(trans.dst, trans); + } + int asd=123; + + } + + public static Transition from(Phase src, Phase dst) { + return m.get(src).get(dst); + } + } + + // Simple demo program - prints a sloppy table + public static void main(String[] args) { + for (Phase src : Phase.values()) + for (Phase dst : Phase.values()) + if (src != dst) + System.out.printf("%s to %s : %s %n", src, dst, + Transition.from(src, dst)); + + int asd=21; + } +} diff --git a/src/main/test/PojoBug.java b/src/main/test/PojoBug.java new file mode 100644 index 0000000..e53478b --- /dev/null +++ b/src/main/test/PojoBug.java @@ -0,0 +1,14 @@ +/** + * Created by inst1 on 2017/7/17. + */ +public class PojoBug { + private aaa aa; + + public aaa getAa() { + return aa; + } + + public void setAa(aaa aa) { + this.aa = aa; + } +} diff --git a/src/main/test/Stack.java b/src/main/test/Stack.java new file mode 100644 index 0000000..4a176d8 --- /dev/null +++ b/src/main/test/Stack.java @@ -0,0 +1,44 @@ +// Can you spot the "memory leak"? + +import java.util.*; + +public class Stack { + private Object[] elements; + private int size = 0; + private static final int DEFAULT_INITIAL_CAPACITY = 16; + + public Stack() { + elements = new Object[DEFAULT_INITIAL_CAPACITY]; + } + + public void push(Object e) { + ensureCapacity(); + elements[size++] = e; + } + + public Object pop() { + if (size == 0) + throw new EmptyStackException(); + return elements[--size]; + } + + /** + * Ensure space for at least one more element, roughly + * doubling the capacity each time the array needs to grow. + */ + private void ensureCapacity() { + if (elements.length == size) + elements = Arrays.copyOf(elements, 2 * size + 1); + } + public static void main(String[] args){ + Stack stack=new Stack(); + while(true){ + for(int i=0;i<16;i++){ + stack.push(new String("addasdasdas")); + } + for(int j=0;j<16;j++){ + stack.pop(); + } + } + } +} diff --git a/src/main/test/Sub.java b/src/main/test/Sub.java new file mode 100644 index 0000000..1babd26 --- /dev/null +++ b/src/main/test/Sub.java @@ -0,0 +1,19 @@ +import java.util.*; + +public class Sub extends Super { + private final Date date; // Blank final, set by constructor + + Sub() { + date = new Date(); + } + + // Overriding method invoked by superclass constructor + @Override public void overrideMe() { + System.out.println(date); + } + + public static void main(String[] args) { + Sub sub = new Sub(); + sub.overrideMe(); + } +} diff --git a/src/main/test/Super.java b/src/main/test/Super.java new file mode 100644 index 0000000..cd5ad1a --- /dev/null +++ b/src/main/test/Super.java @@ -0,0 +1,8 @@ +public class Super { + // Broken - constructor invokes an overridable method + public Super() { + overrideMe(); + } + public void overrideMe() { + } +} diff --git a/src/main/test/TestSum.java b/src/main/test/TestSum.java new file mode 100644 index 0000000..e77305b --- /dev/null +++ b/src/main/test/TestSum.java @@ -0,0 +1,16 @@ +import java.util.Date; + +/** + * Created by inst1 on 2017/7/12. + */ +public class TestSum { + public static void main(String[] args){ + long start=new Date().getTime(); + long sum=0L; + for(long i=0;i + * If the Throwable is an Error, throw it; if it is a + * RuntimeException return it, otherwise throw IllegalStateException + */ + public static RuntimeException launderThrowable(Throwable t) { + if (t instanceof RuntimeException) + return (RuntimeException) t; + else if (t instanceof Error) + throw (Error) t; + else + throw new IllegalStateException("Not unchecked", t); + } +} diff --git a/src/main/test/concurrent/PrimeProducer.java b/src/main/test/concurrent/PrimeProducer.java new file mode 100644 index 0000000..dd53725 --- /dev/null +++ b/src/main/test/concurrent/PrimeProducer.java @@ -0,0 +1,33 @@ +package concurrent; + +import java.math.BigInteger; +import java.util.concurrent.*; + +/** + * PrimeProducer + *

+ * Using interruption for cancellation + * + * @author Brian Goetz and Tim Peierls + */ +public class PrimeProducer extends Thread { + private final BlockingQueue queue; + + PrimeProducer(BlockingQueue queue) { + this.queue = queue; + } + + public void run() { + try { + BigInteger p = BigInteger.ONE; + while (!Thread.currentThread().isInterrupted()) + queue.put(p = p.nextProbablePrime()); + } catch (InterruptedException consumed) { + /* Allow thread to exit */ + } + } + + public void cancel() { + interrupt(); + } +} diff --git a/src/main/test/concurrent/TestHarness.java b/src/main/test/concurrent/TestHarness.java new file mode 100644 index 0000000..82f26eb --- /dev/null +++ b/src/main/test/concurrent/TestHarness.java @@ -0,0 +1,41 @@ +package concurrent; + +import java.util.concurrent.*; + +/** + * TestHarness + *

+ * Using CountDownLatch for starting and stopping threads in timing tests + * + * @author Brian Goetz and Tim Peierls + */ +public class TestHarness { + public long timeTasks(int nThreads, final Runnable task) + throws InterruptedException { + final CountDownLatch startGate = new CountDownLatch(1); + final CountDownLatch endGate = new CountDownLatch(nThreads); + + for (int i = 0; i < nThreads; i++) { + Thread t = new Thread() { + public void run() { + try { + startGate.await(); + try { + task.run(); + } finally { + endGate.countDown(); + } + } catch (InterruptedException ignored) { + } + } + }; + t.start(); + } + + long start = System.nanoTime(); + startGate.countDown(); + endGate.await(); + long end = System.nanoTime(); + return end - start; + } +} diff --git a/src/main/test/concurrent/TestIntterupt.java b/src/main/test/concurrent/TestIntterupt.java new file mode 100644 index 0000000..0ccbe4f --- /dev/null +++ b/src/main/test/concurrent/TestIntterupt.java @@ -0,0 +1,51 @@ +package concurrent; + +/** + * Created by inst1 on 2017/7/25. + */ +public class TestIntterupt implements Runnable { + @Override + public void run() { + try { + another(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + public static void main(String args[]){ + Thread t=new Thread(new TestIntterupt()); + t.start(); + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("开始尝试终止"); + t.interrupt(); + + } + void synchronize() throws InterruptedException { + while(!Thread.currentThread().isInterrupted()){ + + } + throw new InterruptedException(); + + } + void another() throws InterruptedException{ + int i=0; + try { + synchronize(); + while (true) { + i++; + + } + } + catch(InterruptedException e){ + + Thread.currentThread().interrupt(); + } + while(true){ + + } + } +} diff --git a/src/main/test/sdadsad.java b/src/main/test/sdadsad.java new file mode 100644 index 0000000..6261005 --- /dev/null +++ b/src/main/test/sdadsad.java @@ -0,0 +1,55 @@ +/** + * Created by inst1 on 2017/9/17. + */ +public class sdadsad { + public static int Ip2Int(String strIp){ + String[] ss = strIp.split("\\."); + if(ss.length != 4){ + return 0; + } + byte[] bytes = new byte[ss.length]; + for(int i = 0; i < bytes.length; i++){ + bytes[i] = (byte) Integer.parseInt(ss[i]); + } + return byte2Int(bytes); + } + /** + * 将int型ip转成String型ip + * @param intIp + * @return + */ + public static String int2Ip(int intIp){ + byte[] bytes = int2byte(intIp); + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < 4; i++){ + sb.append(bytes[i] & 0xFF); + if(i < 3){ + sb.append("."); + } + } + return sb.toString(); + } + + private static byte[] int2byte(int i) { + byte[] bytes = new byte[4]; + bytes[0] = (byte) (0xff & i); + bytes[1] = (byte) ((0xff00 & i) >> 8); + bytes[2] = (byte) ((0xff0000 & i) >> 16); + bytes[3] = (byte) ((0xff000000 & i) >> 24); + return bytes; + } + private static int byte2Int(byte[] bytes) { + int n = bytes[0] & 0xFF; + n |= ((bytes[1] << 8) & 0xFF00); + n |= ((bytes[2] << 16) & 0xFF0000); + n |= ((bytes[3] << 24) & 0xFF000000); + return n; + } + + public static void main(String[] args) { + String ip1 = "192.168.0.1"; + int intIp = Ip2Int(ip1); + String ip2 = int2Ip(intIp); + System.out.println(ip2); + } +} diff --git a/src/main/test/test.java b/src/main/test/test.java new file mode 100644 index 0000000..1235538 --- /dev/null +++ b/src/main/test/test.java @@ -0,0 +1,16 @@ +/** + * Created by inst1 on 2017/7/16. + */ +public class test +{ + static int i=0; + + public static void main(String args[]){ + eee(++i); + + } + + static void eee(int i){ + System.out.print(i); + } +} diff --git a/src/main/test/testThread.java b/src/main/test/testThread.java new file mode 100644 index 0000000..844dcb1 --- /dev/null +++ b/src/main/test/testThread.java @@ -0,0 +1,17 @@ +/** + * Created by inst1 on 2017/7/16. + */ +public class testThread implements Runnable { + int count=0; + + @Override + public void run() { + for(int i=0;i<10000;i++) { + add(); + } + } + public void add(){ + count++; + System.out.println(this.hashCode() + ":" + count); + } +} diff --git a/src/main/webapp/WEB-INF/views/echarts.js b/src/main/webapp/WEB-INF/views/echarts.js new file mode 100644 index 0000000..60c5d61 --- /dev/null +++ b/src/main/webapp/WEB-INF/views/echarts.js @@ -0,0 +1,63738 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["echarts"] = factory(); + else + root["echarts"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Export echarts as CommonJS module + */ + module.exports = __webpack_require__(1); + + // Import all charts and components + __webpack_require__(99); + __webpack_require__(133); + __webpack_require__(138); + __webpack_require__(147); + __webpack_require__(151); + + __webpack_require__(161); + __webpack_require__(182); + __webpack_require__(194); + __webpack_require__(215); + __webpack_require__(219); + __webpack_require__(223); + __webpack_require__(238); + __webpack_require__(244); + __webpack_require__(251); + __webpack_require__(257); + __webpack_require__(261); + __webpack_require__(269); + + __webpack_require__(112); + __webpack_require__(273); + __webpack_require__(279); + __webpack_require__(283); + __webpack_require__(294); + __webpack_require__(224); + __webpack_require__(297); + __webpack_require__(303); + + __webpack_require__(315); + + __webpack_require__(316); + __webpack_require__(330); + + __webpack_require__(345); + __webpack_require__(351); + __webpack_require__(354); + + __webpack_require__(357); + __webpack_require__(366); + + __webpack_require__(379); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + // Enable DEV mode when using source code without build. which has no __DEV__ variable + // In build process 'typeof __DEV__' will be replace with 'boolean' + // So this code will be removed or disabled anyway after built. + if (false) { + // In browser + if (typeof window !== 'undefined') { + window.__DEV__ = true; + } + // In node + else if (typeof global !== 'undefined') { + global.__DEV__ = true; + } + } + + /*! + * ECharts, a javascript interactive chart library. + * + * Copyright (c) 2015, Baidu Inc. + * All rights reserved. + * + * LICENSE + * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt + */ + + /** + * @module echarts + */ + + + var env = __webpack_require__(2); + + var GlobalModel = __webpack_require__(3); + var ExtensionAPI = __webpack_require__(25); + var CoordinateSystemManager = __webpack_require__(26); + var OptionManager = __webpack_require__(27); + + var ComponentModel = __webpack_require__(19); + var SeriesModel = __webpack_require__(28); + + var ComponentView = __webpack_require__(29); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + + var zrender = __webpack_require__(81); + var zrUtil = __webpack_require__(4); + var colorTool = __webpack_require__(39); + var Eventful = __webpack_require__(33); + var timsort = __webpack_require__(85); + + var each = zrUtil.each; + + var PRIORITY_PROCESSOR_FILTER = 1000; + var PRIORITY_PROCESSOR_STATISTIC = 5000; + + + var PRIORITY_VISUAL_LAYOUT = 1000; + var PRIORITY_VISUAL_GLOBAL = 2000; + var PRIORITY_VISUAL_CHART = 3000; + var PRIORITY_VISUAL_COMPONENT = 4000; + var PRIORITY_VISUAL_BRUSH = 5000; + + // Main process have three entries: `setOption`, `dispatchAction` and `resize`, + // where they must not be invoked nestedly, except the only case: invoke + // dispatchAction with updateMethod "none" in main process. + // This flag is used to carry out this rule. + // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). + var IN_MAIN_PROCESS = '__flag_in_main_process'; + var HAS_GRADIENT_OR_PATTERN_BG = '_hasGradientOrPatternBg'; + + + var OPTION_UPDATED = '_optionUpdated'; + + function createRegisterEventWithLowercaseName(method) { + return function (eventName, handler, context) { + // Event name is all lowercase + eventName = eventName && eventName.toLowerCase(); + Eventful.prototype[method].call(this, eventName, handler, context); + }; + } + /** + * @module echarts~MessageCenter + */ + function MessageCenter() { + Eventful.call(this); + } + MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on'); + MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off'); + MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one'); + zrUtil.mixin(MessageCenter, Eventful); + /** + * @module echarts~ECharts + */ + function ECharts (dom, theme, opts) { + opts = opts || {}; + + // Get theme by name + if (typeof theme === 'string') { + theme = themeStorage[theme]; + } + + /** + * @type {string} + */ + this.id; + /** + * Group id + * @type {string} + */ + this.group; + /** + * @type {HTMLDomElement} + * @private + */ + this._dom = dom; + /** + * @type {module:zrender/ZRender} + * @private + */ + this._zr = zrender.init(dom, { + renderer: opts.renderer || 'canvas', + devicePixelRatio: opts.devicePixelRatio + }); + + /** + * @type {Object} + * @private + */ + this._theme = zrUtil.clone(theme); + + /** + * @type {Array.} + * @private + */ + this._chartsViews = []; + + /** + * @type {Object.} + * @private + */ + this._chartsMap = {}; + + /** + * @type {Array.} + * @private + */ + this._componentsViews = []; + + /** + * @type {Object.} + * @private + */ + this._componentsMap = {}; + + /** + * @type {module:echarts/ExtensionAPI} + * @private + */ + this._api = new ExtensionAPI(this); + + /** + * @type {module:echarts/CoordinateSystem} + * @private + */ + this._coordSysMgr = new CoordinateSystemManager(); + + Eventful.call(this); + + /** + * @type {module:echarts~MessageCenter} + * @private + */ + this._messageCenter = new MessageCenter(); + + // Init mouse events + this._initEvents(); + + // In case some people write `window.onresize = chart.resize` + this.resize = zrUtil.bind(this.resize, this); + + // Can't dispatch action during rendering procedure + this._pendingActions = []; + // Sort on demand + function prioritySortFunc(a, b) { + return a.prio - b.prio; + } + timsort(visualFuncs, prioritySortFunc); + timsort(dataProcessorFuncs, prioritySortFunc); + + this._zr.animation.on('frame', this._onframe, this); + } + + var echartsProto = ECharts.prototype; + + echartsProto._onframe = function () { + // Lazy update + if (this[OPTION_UPDATED]) { + + this[IN_MAIN_PROCESS] = true; + + updateMethods.prepareAndUpdate.call(this); + + this[IN_MAIN_PROCESS] = false; + + this[OPTION_UPDATED] = false; + } + }; + /** + * @return {HTMLDomElement} + */ + echartsProto.getDom = function () { + return this._dom; + }; + + /** + * @return {module:zrender~ZRender} + */ + echartsProto.getZr = function () { + return this._zr; + }; + + /** + * @param {Object} option + * @param {boolean} notMerge + * @param {boolean} [lazyUpdate=false] Useful when setOption frequently. + */ + echartsProto.setOption = function (option, notMerge, lazyUpdate) { + if (true) { + zrUtil.assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.'); + } + + this[IN_MAIN_PROCESS] = true; + + if (!this._model || notMerge) { + var optionManager = new OptionManager(this._api); + var theme = this._theme; + var ecModel = this._model = new GlobalModel(null, null, theme, optionManager); + ecModel.init(null, null, theme, optionManager); + } + + this._model.setOption(option, optionPreprocessorFuncs); + + if (lazyUpdate) { + this[OPTION_UPDATED] = true; + } + else { + updateMethods.prepareAndUpdate.call(this); + this._zr.refreshImmediately(); + this[OPTION_UPDATED] = false; + } + + this[IN_MAIN_PROCESS] = false; + + this._flushPendingActions(); + }; + + /** + * @DEPRECATED + */ + echartsProto.setTheme = function () { + console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); + }; + + /** + * @return {module:echarts/model/Global} + */ + echartsProto.getModel = function () { + return this._model; + }; + + /** + * @return {Object} + */ + echartsProto.getOption = function () { + return this._model && this._model.getOption(); + }; + + /** + * @return {number} + */ + echartsProto.getWidth = function () { + return this._zr.getWidth(); + }; + + /** + * @return {number} + */ + echartsProto.getHeight = function () { + return this._zr.getHeight(); + }; + + /** + * Get canvas which has all thing rendered + * @param {Object} opts + * @param {string} [opts.backgroundColor] + */ + echartsProto.getRenderedCanvas = function (opts) { + if (!env.canvasSupported) { + return; + } + opts = opts || {}; + opts.pixelRatio = opts.pixelRatio || 1; + opts.backgroundColor = opts.backgroundColor + || this._model.get('backgroundColor'); + var zr = this._zr; + var list = zr.storage.getDisplayList(); + // Stop animations + zrUtil.each(list, function (el) { + el.stopAnimation(true); + }); + return zr.painter.getRenderedCanvas(opts); + }; + /** + * @return {string} + * @param {Object} opts + * @param {string} [opts.type='png'] + * @param {string} [opts.pixelRatio=1] + * @param {string} [opts.backgroundColor] + */ + echartsProto.getDataURL = function (opts) { + opts = opts || {}; + var excludeComponents = opts.excludeComponents; + var ecModel = this._model; + var excludesComponentViews = []; + var self = this; + + each(excludeComponents, function (componentType) { + ecModel.eachComponent({ + mainType: componentType + }, function (component) { + var view = self._componentsMap[component.__viewId]; + if (!view.group.ignore) { + excludesComponentViews.push(view); + view.group.ignore = true; + } + }); + }); + + var url = this.getRenderedCanvas(opts).toDataURL( + 'image/' + (opts && opts.type || 'png') + ); + + each(excludesComponentViews, function (view) { + view.group.ignore = false; + }); + return url; + }; + + + /** + * @return {string} + * @param {Object} opts + * @param {string} [opts.type='png'] + * @param {string} [opts.pixelRatio=1] + * @param {string} [opts.backgroundColor] + */ + echartsProto.getConnectedDataURL = function (opts) { + if (!env.canvasSupported) { + return; + } + var groupId = this.group; + var mathMin = Math.min; + var mathMax = Math.max; + var MAX_NUMBER = Infinity; + if (connectedGroups[groupId]) { + var left = MAX_NUMBER; + var top = MAX_NUMBER; + var right = -MAX_NUMBER; + var bottom = -MAX_NUMBER; + var canvasList = []; + var dpr = (opts && opts.pixelRatio) || 1; + for (var id in instances) { + var chart = instances[id]; + if (chart.group === groupId) { + var canvas = chart.getRenderedCanvas( + zrUtil.clone(opts) + ); + var boundingRect = chart.getDom().getBoundingClientRect(); + left = mathMin(boundingRect.left, left); + top = mathMin(boundingRect.top, top); + right = mathMax(boundingRect.right, right); + bottom = mathMax(boundingRect.bottom, bottom); + canvasList.push({ + dom: canvas, + left: boundingRect.left, + top: boundingRect.top + }); + } + } + + left *= dpr; + top *= dpr; + right *= dpr; + bottom *= dpr; + var width = right - left; + var height = bottom - top; + var targetCanvas = zrUtil.createCanvas(); + targetCanvas.width = width; + targetCanvas.height = height; + var zr = zrender.init(targetCanvas); + + each(canvasList, function (item) { + var img = new graphic.Image({ + style: { + x: item.left * dpr - left, + y: item.top * dpr - top, + image: item.dom + } + }); + zr.add(img); + }); + zr.refreshImmediately(); + + return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); + } + else { + return this.getDataURL(opts); + } + }; + + var updateMethods = { + + + /** + * @param {Object} payload + * @private + */ + update: function (payload) { + // console.time && console.time('update'); + + var ecModel = this._model; + var api = this._api; + var coordSysMgr = this._coordSysMgr; + var zr = this._zr; + // update before setOption + if (!ecModel) { + return; + } + + // Fixme First time update ? + ecModel.restoreData(); + + // TODO + // Save total ecModel here for undo/redo (after restoring data and before processing data). + // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. + + // Create new coordinate system each update + // In LineView may save the old coordinate system and use it to get the orignal point + coordSysMgr.create(this._model, this._api); + + processData.call(this, ecModel, api); + + stackSeriesData.call(this, ecModel); + + coordSysMgr.update(ecModel, api); + + doVisualEncoding.call(this, ecModel, payload); + + doRender.call(this, ecModel, payload); + + // Set background + var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; + + var painter = zr.painter; + // TODO all use clearColor ? + if (painter.isSingleCanvas && painter.isSingleCanvas()) { + zr.configLayer(0, { + clearColor: backgroundColor + }); + } + else { + // In IE8 + if (!env.canvasSupported) { + var colorArr = colorTool.parse(backgroundColor); + backgroundColor = colorTool.stringify(colorArr, 'rgb'); + if (colorArr[3] === 0) { + backgroundColor = 'transparent'; + } + } + if (backgroundColor.colorStops || backgroundColor.image) { + // Gradient background + // FIXME Fixed layer? + zr.configLayer(0, { + clearColor: backgroundColor + }); + this[HAS_GRADIENT_OR_PATTERN_BG] = true; + + this._dom.style.background = 'transparent'; + } + else { + if (this[HAS_GRADIENT_OR_PATTERN_BG]) { + zr.configLayer(0, { + clearColor: null + }); + } + this[HAS_GRADIENT_OR_PATTERN_BG] = false; + + this._dom.style.background = backgroundColor; + } + } + + // console.time && console.timeEnd('update'); + }, + + // PENDING + /** + * @param {Object} payload + * @private + */ + updateView: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + ecModel.eachSeries(function (seriesModel) { + seriesModel.getData().clearAllVisual(); + }); + + doVisualEncoding.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateView', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + updateVisual: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + ecModel.eachSeries(function (seriesModel) { + seriesModel.getData().clearAllVisual(); + }); + + doVisualEncoding.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + updateLayout: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + doLayout.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + highlight: function (payload) { + toggleHighlight.call(this, 'highlight', payload); + }, + + /** + * @param {Object} payload + * @private + */ + downplay: function (payload) { + toggleHighlight.call(this, 'downplay', payload); + }, + + /** + * @param {Object} payload + * @private + */ + prepareAndUpdate: function (payload) { + var ecModel = this._model; + + prepareView.call(this, 'component', ecModel); + + prepareView.call(this, 'chart', ecModel); + + updateMethods.update.call(this, payload); + } + }; + + /** + * @param {Object} payload + * @private + */ + function toggleHighlight(method, payload) { + var ecModel = this._model; + + // dispatchAction before setOption + if (!ecModel) { + return; + } + + ecModel.eachComponent( + {mainType: 'series', query: payload}, + function (seriesModel, index) { + var chartView = this._chartsMap[seriesModel.__viewId]; + if (chartView && chartView.__alive) { + chartView[method]( + seriesModel, ecModel, this._api, payload + ); + } + }, + this + ); + } + + /** + * Resize the chart + */ + echartsProto.resize = function () { + if (true) { + zrUtil.assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.'); + } + + this[IN_MAIN_PROCESS] = true; + + this._zr.resize(); + + var optionChanged = this._model && this._model.resetOption('media'); + updateMethods[optionChanged ? 'prepareAndUpdate' : 'update'].call(this); + + // Resize loading effect + this._loadingFX && this._loadingFX.resize(); + + this[IN_MAIN_PROCESS] = false; + + this._flushPendingActions(); + }; + + /** + * Show loading effect + * @param {string} [name='default'] + * @param {Object} [cfg] + */ + echartsProto.showLoading = function (name, cfg) { + if (zrUtil.isObject(name)) { + cfg = name; + name = ''; + } + name = name || 'default'; + + this.hideLoading(); + if (!loadingEffects[name]) { + if (true) { + console.warn('Loading effects ' + name + ' not exists.'); + } + return; + } + var el = loadingEffects[name](this._api, cfg); + var zr = this._zr; + this._loadingFX = el; + + zr.add(el); + }; + + /** + * Hide loading effect + */ + echartsProto.hideLoading = function () { + this._loadingFX && this._zr.remove(this._loadingFX); + this._loadingFX = null; + }; + + /** + * @param {Object} eventObj + * @return {Object} + */ + echartsProto.makeActionFromEvent = function (eventObj) { + var payload = zrUtil.extend({}, eventObj); + payload.type = eventActionMap[eventObj.type]; + return payload; + }; + + /** + * @pubilc + * @param {Object} payload + * @param {string} [payload.type] Action type + * @param {boolean} [silent=false] Whether trigger event. + */ + echartsProto.dispatchAction = function (payload, silent) { + var actionWrap = actions[payload.type]; + if (!actionWrap) { + return; + } + + var actionInfo = actionWrap.actionInfo; + var updateMethod = actionInfo.update || 'update'; + + // if (__DEV__) { + // zrUtil.assert( + // !this[IN_MAIN_PROCESS], + // '`dispatchAction` should not be called during main process.' + // + 'unless updateMathod is "none".' + // ); + // } + + // May dispatchAction in rendering procedure + if (this[IN_MAIN_PROCESS]) { + this._pendingActions.push(payload); + return; + } + + this[IN_MAIN_PROCESS] = true; + + var payloads = [payload]; + var batched = false; + // Batch action + if (payload.batch) { + batched = true; + payloads = zrUtil.map(payload.batch, function (item) { + item = zrUtil.defaults(zrUtil.extend({}, item), payload); + item.batch = null; + return item; + }); + } + + var eventObjBatch = []; + var eventObj; + var isHighlightOrDownplay = payload.type === 'highlight' || payload.type === 'downplay'; + for (var i = 0; i < payloads.length; i++) { + var batchItem = payloads[i]; + // Action can specify the event by return it. + eventObj = actionWrap.action(batchItem, this._model); + // Emit event outside + eventObj = eventObj || zrUtil.extend({}, batchItem); + // Convert type to eventType + eventObj.type = actionInfo.event || eventObj.type; + eventObjBatch.push(eventObj); + + // Highlight and downplay are special. + isHighlightOrDownplay && updateMethods[updateMethod].call(this, batchItem); + } + + if (updateMethod !== 'none' && !isHighlightOrDownplay) { + // Still dirty + if (this[OPTION_UPDATED]) { + // FIXME Pass payload ? + updateMethods.prepareAndUpdate.call(this, payload); + this[OPTION_UPDATED] = false; + } + else { + updateMethods[updateMethod].call(this, payload); + } + } + + // Follow the rule of action batch + if (batched) { + eventObj = { + type: actionInfo.event || payload.type, + batch: eventObjBatch + }; + } + else { + eventObj = eventObjBatch[0]; + } + + this[IN_MAIN_PROCESS] = false; + + !silent && this._messageCenter.trigger(eventObj.type, eventObj); + + this._flushPendingActions(); + + }; + + echartsProto._flushPendingActions = function () { + var pendingActions = this._pendingActions; + while (pendingActions.length) { + var payload = pendingActions.shift(); + this.dispatchAction(payload); + } + }; + + /** + * Register event + * @method + */ + echartsProto.on = createRegisterEventWithLowercaseName('on'); + echartsProto.off = createRegisterEventWithLowercaseName('off'); + echartsProto.one = createRegisterEventWithLowercaseName('one'); + + /** + * @param {string} methodName + * @private + */ + function invokeUpdateMethod(methodName, ecModel, payload) { + var api = this._api; + + // Update all components + each(this._componentsViews, function (component) { + var componentModel = component.__model; + component[methodName](componentModel, ecModel, api, payload); + + updateZ(componentModel, component); + }, this); + + // Upate all charts + ecModel.eachSeries(function (seriesModel, idx) { + var chart = this._chartsMap[seriesModel.__viewId]; + chart[methodName](seriesModel, ecModel, api, payload); + + updateZ(seriesModel, chart); + + updateProgressiveAndBlend(seriesModel, chart); + }, this); + + // If use hover layer + updateHoverLayerStatus(this._zr, ecModel); + } + + /** + * Prepare view instances of charts and components + * @param {module:echarts/model/Global} ecModel + * @private + */ + function prepareView(type, ecModel) { + var isComponent = type === 'component'; + var viewList = isComponent ? this._componentsViews : this._chartsViews; + var viewMap = isComponent ? this._componentsMap : this._chartsMap; + var zr = this._zr; + + for (var i = 0; i < viewList.length; i++) { + viewList[i].__alive = false; + } + + ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) { + if (isComponent) { + if (componentType === 'series') { + return; + } + } + else { + model = componentType; + } + + // Consider: id same and type changed. + var viewId = model.id + '_' + model.type; + var view = viewMap[viewId]; + if (!view) { + var classType = ComponentModel.parseClassType(model.type); + var Clazz = isComponent + ? ComponentView.getClass(classType.main, classType.sub) + : ChartView.getClass(classType.sub); + if (Clazz) { + view = new Clazz(); + view.init(ecModel, this._api); + viewMap[viewId] = view; + viewList.push(view); + zr.add(view.group); + } + else { + // Error + return; + } + } + + model.__viewId = viewId; + view.__alive = true; + view.__id = viewId; + view.__model = model; + }, this); + + for (var i = 0; i < viewList.length;) { + var view = viewList[i]; + if (!view.__alive) { + zr.remove(view.group); + view.dispose(ecModel, this._api); + viewList.splice(i, 1); + delete viewMap[view.__id]; + } + else { + i++; + } + } + } + + /** + * Processor data in each series + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function processData(ecModel, api) { + each(dataProcessorFuncs, function (process) { + process.func(ecModel, api); + }); + } + + /** + * @private + */ + function stackSeriesData(ecModel) { + var stackedDataMap = {}; + ecModel.eachSeries(function (series) { + var stack = series.get('stack'); + var data = series.getData(); + if (stack && data.type === 'list') { + var previousStack = stackedDataMap[stack]; + if (previousStack) { + data.stackedOn = previousStack; + } + stackedDataMap[stack] = data; + } + }); + } + + /** + * Layout before each chart render there series, special visual encoding stage + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function doLayout(ecModel, payload) { + var api = this._api; + each(visualFuncs, function (visual) { + if (visual.isLayout) { + visual.func(ecModel, api, payload); + } + }); + } + + /** + * Encode visual infomation from data after data processing + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function doVisualEncoding(ecModel, payload) { + var api = this._api; + ecModel.clearColorPalette(); + ecModel.eachSeries(function (seriesModel) { + seriesModel.clearColorPalette(); + }); + each(visualFuncs, function (visual) { + visual.func(ecModel, api, payload); + }); + } + + /** + * Render each chart and component + * @private + */ + function doRender(ecModel, payload) { + var api = this._api; + // Render all components + each(this._componentsViews, function (componentView) { + var componentModel = componentView.__model; + componentView.render(componentModel, ecModel, api, payload); + + updateZ(componentModel, componentView); + }, this); + + each(this._chartsViews, function (chart) { + chart.__alive = false; + }, this); + + // Render all charts + ecModel.eachSeries(function (seriesModel, idx) { + var chartView = this._chartsMap[seriesModel.__viewId]; + chartView.__alive = true; + chartView.render(seriesModel, ecModel, api, payload); + + chartView.group.silent = !!seriesModel.get('silent'); + + updateZ(seriesModel, chartView); + + updateProgressiveAndBlend(seriesModel, chartView); + + }, this); + + // If use hover layer + updateHoverLayerStatus(this._zr, ecModel); + + // Remove groups of unrendered charts + each(this._chartsViews, function (chart) { + if (!chart.__alive) { + chart.remove(ecModel, api); + } + }, this); + } + + var MOUSE_EVENT_NAMES = [ + 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout' + ]; + /** + * @private + */ + echartsProto._initEvents = function () { + each(MOUSE_EVENT_NAMES, function (eveName) { + this._zr.on(eveName, function (e) { + var ecModel = this.getModel(); + var el = e.target; + if (el && el.dataIndex != null) { + var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); + var params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {}; + params.event = e; + params.type = eveName; + this.trigger(eveName, params); + } + // If element has custom eventData of components + else if (el && el.eventData) { + this.trigger(eveName, el.eventData); + } + }, this); + }, this); + + each(eventActionMap, function (actionType, eventType) { + this._messageCenter.on(eventType, function (event) { + this.trigger(eventType, event); + }, this); + }, this); + }; + + /** + * @return {boolean} + */ + echartsProto.isDisposed = function () { + return this._disposed; + }; + + /** + * Clear + */ + echartsProto.clear = function () { + this.setOption({ series: [] }, true); + }; + /** + * Dispose instance + */ + echartsProto.dispose = function () { + if (this._disposed) { + if (true) { + console.warn('Instance ' + this.id + ' has been disposed'); + } + return; + } + this._disposed = true; + + var api = this._api; + var ecModel = this._model; + + each(this._componentsViews, function (component) { + component.dispose(ecModel, api); + }); + each(this._chartsViews, function (chart) { + chart.dispose(ecModel, api); + }); + + // Dispose after all views disposed + this._zr.dispose(); + + delete instances[this.id]; + }; + + zrUtil.mixin(ECharts, Eventful); + + function updateHoverLayerStatus(zr, ecModel) { + var storage = zr.storage; + var elCount = 0; + storage.traverse(function (el) { + if (!el.isGroup) { + elCount++; + } + }); + if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) { + storage.traverse(function (el) { + if (!el.isGroup) { + el.useHoverLayer = true; + } + }); + } + } + /** + * Update chart progressive and blend. + * @param {module:echarts/model/Series|module:echarts/model/Component} model + * @param {module:echarts/view/Component|module:echarts/view/Chart} view + */ + function updateProgressiveAndBlend(seriesModel, chartView) { + // Progressive configuration + var elCount = 0; + chartView.group.traverse(function (el) { + if (el.type !== 'group' && !el.ignore) { + elCount++; + } + }); + var frameDrawNum = +seriesModel.get('progressive'); + var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node; + if (needProgressive) { + chartView.group.traverse(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + el.progressive = needProgressive ? + Math.floor(elCount++ / frameDrawNum) : -1; + if (needProgressive) { + el.stopAnimation(true); + } + } + }); + } + + // Blend configration + var blendMode = seriesModel.get('blendMode') || null; + if (true) { + if (!env.canvasSupported && blendMode && blendMode !== 'source-over') { + console.warn('Only canvas support blendMode'); + } + } + chartView.group.traverse(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + el.setStyle('blend', blendMode); + } + }); + } + /** + * @param {module:echarts/model/Series|module:echarts/model/Component} model + * @param {module:echarts/view/Component|module:echarts/view/Chart} view + */ + function updateZ(model, view) { + var z = model.get('z'); + var zlevel = model.get('zlevel'); + // Set z and zlevel + view.group.traverse(function (el) { + if (el.type !== 'group') { + z != null && (el.z = z); + zlevel != null && (el.zlevel = zlevel); + } + }); + } + /** + * @type {Array.} + * @inner + */ + var actions = []; + + /** + * Map eventType to actionType + * @type {Object} + */ + var eventActionMap = {}; + + /** + * Data processor functions of each stage + * @type {Array.>} + * @inner + */ + var dataProcessorFuncs = []; + + /** + * @type {Array.} + * @inner + */ + var optionPreprocessorFuncs = []; + + /** + * Visual encoding functions of each stage + * @type {Array.>} + * @inner + */ + var visualFuncs = []; + /** + * Theme storage + * @type {Object.} + */ + var themeStorage = {}; + /** + * Loading effects + */ + var loadingEffects = {}; + + + var instances = {}; + var connectedGroups = {}; + + var idBase = new Date() - 0; + var groupIdBase = new Date() - 0; + var DOM_ATTRIBUTE_KEY = '_echarts_instance_'; + /** + * @alias module:echarts + */ + var echarts = { + /** + * @type {number} + */ + version: '3.2.3', + dependencies: { + zrender: '3.1.3' + } + }; + + function enableConnect(chart) { + + var STATUS_PENDING = 0; + var STATUS_UPDATING = 1; + var STATUS_UPDATED = 2; + var STATUS_KEY = '__connectUpdateStatus'; + function updateConnectedChartsStatus(charts, status) { + for (var i = 0; i < charts.length; i++) { + var otherChart = charts[i]; + otherChart[STATUS_KEY] = status; + } + } + zrUtil.each(eventActionMap, function (actionType, eventType) { + chart._messageCenter.on(eventType, function (event) { + if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) { + var action = chart.makeActionFromEvent(event); + var otherCharts = []; + for (var id in instances) { + var otherChart = instances[id]; + if (otherChart !== chart && otherChart.group === chart.group) { + otherCharts.push(otherChart); + } + } + updateConnectedChartsStatus(otherCharts, STATUS_PENDING); + each(otherCharts, function (otherChart) { + if (otherChart[STATUS_KEY] !== STATUS_UPDATING) { + otherChart.dispatchAction(action); + } + }); + updateConnectedChartsStatus(otherCharts, STATUS_UPDATED); + } + }); + }); + + } + /** + * @param {HTMLDomElement} dom + * @param {Object} [theme] + * @param {Object} opts + */ + echarts.init = function (dom, theme, opts) { + if (true) { + // Check version + if ((zrender.version.replace('.', '') - 0) < (echarts.dependencies.zrender.replace('.', '') - 0)) { + throw new Error( + 'ZRender ' + zrender.version + + ' is too old for ECharts ' + echarts.version + + '. Current version need ZRender ' + + echarts.dependencies.zrender + '+' + ); + } + if (!dom) { + throw new Error('Initialize failed: invalid dom.'); + } + if (zrUtil.isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth || !dom.clientHeight)) { + console.warn('Can\'t get dom width or height'); + } + } + + var chart = new ECharts(dom, theme, opts); + chart.id = 'ec_' + idBase++; + instances[chart.id] = chart; + + dom.setAttribute && + dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id); + + enableConnect(chart); + + return chart; + }; + + /** + * @return {string|Array.} groupId + */ + echarts.connect = function (groupId) { + // Is array of charts + if (zrUtil.isArray(groupId)) { + var charts = groupId; + groupId = null; + // If any chart has group + zrUtil.each(charts, function (chart) { + if (chart.group != null) { + groupId = chart.group; + } + }); + groupId = groupId || ('g_' + groupIdBase++); + zrUtil.each(charts, function (chart) { + chart.group = groupId; + }); + } + connectedGroups[groupId] = true; + return groupId; + }; + + /** + * @return {string} groupId + */ + echarts.disConnect = function (groupId) { + connectedGroups[groupId] = false; + }; + + /** + * Dispose a chart instance + * @param {module:echarts~ECharts|HTMLDomElement|string} chart + */ + echarts.dispose = function (chart) { + if (zrUtil.isDom(chart)) { + chart = echarts.getInstanceByDom(chart); + } + else if (typeof chart === 'string') { + chart = instances[chart]; + } + if ((chart instanceof ECharts) && !chart.isDisposed()) { + chart.dispose(); + } + }; + + /** + * @param {HTMLDomElement} dom + * @return {echarts~ECharts} + */ + echarts.getInstanceByDom = function (dom) { + var key = dom.getAttribute(DOM_ATTRIBUTE_KEY); + return instances[key]; + }; + /** + * @param {string} key + * @return {echarts~ECharts} + */ + echarts.getInstanceById = function (key) { + return instances[key]; + }; + + /** + * Register theme + */ + echarts.registerTheme = function (name, theme) { + themeStorage[name] = theme; + }; + + /** + * Register option preprocessor + * @param {Function} preprocessorFunc + */ + echarts.registerPreprocessor = function (preprocessorFunc) { + optionPreprocessorFuncs.push(preprocessorFunc); + }; + + /** + * @param {number} [priority=1000] + * @param {Function} processorFunc + */ + echarts.registerProcessor = function (priority, processorFunc) { + if (typeof priority === 'function') { + processorFunc = priority; + priority = PRIORITY_PROCESSOR_FILTER; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown processor priority'); + } + } + dataProcessorFuncs.push({ + prio: priority, + func: processorFunc + }); + }; + + /** + * Usage: + * registerAction('someAction', 'someEvent', function () { ... }); + * registerAction('someAction', function () { ... }); + * registerAction( + * {type: 'someAction', event: 'someEvent', update: 'updateView'}, + * function () { ... } + * ); + * + * @param {(string|Object)} actionInfo + * @param {string} actionInfo.type + * @param {string} [actionInfo.event] + * @param {string} [actionInfo.update] + * @param {string} [eventName] + * @param {Function} action + */ + echarts.registerAction = function (actionInfo, eventName, action) { + if (typeof eventName === 'function') { + action = eventName; + eventName = ''; + } + var actionType = zrUtil.isObject(actionInfo) + ? actionInfo.type + : ([actionInfo, actionInfo = { + event: eventName + }][0]); + + // Event name is all lowercase + actionInfo.event = (actionInfo.event || actionType).toLowerCase(); + eventName = actionInfo.event; + + if (!actions[actionType]) { + actions[actionType] = {action: action, actionInfo: actionInfo}; + } + eventActionMap[eventName] = actionType; + }; + + /** + * @param {string} type + * @param {*} CoordinateSystem + */ + echarts.registerCoordinateSystem = function (type, CoordinateSystem) { + CoordinateSystemManager.register(type, CoordinateSystem); + }; + + /** + * Layout is a special stage of visual encoding + * Most visual encoding like color are common for different chart + * But each chart has it's own layout algorithm + * + * @param {number} [priority=1000] + * @param {Function} layoutFunc + */ + echarts.registerLayout = function (priority, layoutFunc) { + if (typeof priority === 'function') { + layoutFunc = priority; + priority = PRIORITY_VISUAL_LAYOUT; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown layout priority'); + } + } + visualFuncs.push({ + prio: priority, + func: layoutFunc, + isLayout: true + }); + }; + + /** + * @param {number} [priority=3000] + * @param {Function} visualFunc + */ + echarts.registerVisual = function (priority, visualFunc) { + if (typeof priority === 'function') { + visualFunc = priority; + priority = PRIORITY_VISUAL_CHART; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown visual priority'); + } + } + visualFuncs.push({ + prio: priority, + func: visualFunc + }); + }; + + /** + * @param {string} name + */ + echarts.registerLoading = function (name, loadingFx) { + loadingEffects[name] = loadingFx; + }; + + + var parseClassType = ComponentModel.parseClassType; + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendComponentModel = function (opts, superClass) { + var Clazz = ComponentModel; + if (superClass) { + var classType = parseClassType(superClass); + Clazz = ComponentModel.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendComponentView = function (opts, superClass) { + var Clazz = ComponentView; + if (superClass) { + var classType = parseClassType(superClass); + Clazz = ComponentView.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendSeriesModel = function (opts, superClass) { + var Clazz = SeriesModel; + if (superClass) { + superClass = 'series.' + superClass.replace('series.', ''); + var classType = parseClassType(superClass); + Clazz = SeriesModel.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendChartView = function (opts, superClass) { + var Clazz = ChartView; + if (superClass) { + superClass.replace('series.', ''); + var classType = parseClassType(superClass); + Clazz = ChartView.getClass(classType.main, true); + } + return Clazz.extend(opts); + }; + + /** + * ZRender need a canvas context to do measureText. + * But in node environment canvas may be created by node-canvas. + * So we need to specify how to create a canvas instead of using document.createElement('canvas') + * + * Be careful of using it in the browser. + * + * @param {Function} creator + * @example + * var Canvas = require('canvas'); + * var echarts = require('echarts'); + * echarts.setCanvasCreator(function () { + * // Small size is enough. + * return new Canvas(32, 32); + * }); + */ + echarts.setCanvasCreator = function (creator) { + zrUtil.createCanvas = creator; + }; + + echarts.registerVisual(PRIORITY_VISUAL_GLOBAL, __webpack_require__(93)); + echarts.registerPreprocessor(__webpack_require__(94)); + echarts.registerLoading('default', __webpack_require__(96)); + + // Default action + echarts.registerAction({ + type: 'highlight', + event: 'highlight', + update: 'highlight' + }, zrUtil.noop); + echarts.registerAction({ + type: 'downplay', + event: 'downplay', + update: 'downplay' + }, zrUtil.noop); + + + // -------- + // Exports + // -------- + // + echarts.List = __webpack_require__(97); + echarts.Model = __webpack_require__(12); + + echarts.graphic = __webpack_require__(43); + echarts.number = __webpack_require__(7); + echarts.format = __webpack_require__(6); + echarts.matrix = __webpack_require__(11); + echarts.vector = __webpack_require__(10); + echarts.color = __webpack_require__(39); + + echarts.util = {}; + each([ + 'map', 'each', 'filter', 'indexOf', 'inherits', + 'reduce', 'filter', 'bind', 'curry', 'isArray', + 'isString', 'isObject', 'isFunction', 'extend', 'defaults' + ], + function (name) { + echarts.util[name] = zrUtil[name]; + } + ); + + // PRIORITY + echarts.PRIORITY = { + PROCESSOR: { + FILTER: PRIORITY_PROCESSOR_FILTER, + STATISTIC: PRIORITY_PROCESSOR_STATISTIC + }, + VISUAL: { + LAYOUT: PRIORITY_VISUAL_LAYOUT, + GLOBAL: PRIORITY_VISUAL_GLOBAL, + CHART: PRIORITY_VISUAL_CHART, + COMPONENT: PRIORITY_VISUAL_COMPONENT, + BRUSH: PRIORITY_VISUAL_BRUSH + } + }; + + module.exports = echarts; + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + /** + * echarts设备环境识别 + * + * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。 + * @author firede[firede@firede.us] + * @desc thanks zepto. + */ + + var env = {}; + if (typeof navigator === 'undefined') { + // In node + env = { + browser: {}, + os: {}, + node: true, + // Assume canvas is supported + canvasSupported: true + }; + } + else { + env = detect(navigator.userAgent); + } + + module.exports = env; + + // Zepto.js + // (c) 2010-2013 Thomas Fuchs + // Zepto.js may be freely distributed under the MIT license. + + function detect(ua) { + var os = {}; + var browser = {}; + // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/); + // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); + // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); + // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); + // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); + // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/); + // var touchpad = webos && ua.match(/TouchPad/); + // var kindle = ua.match(/Kindle\/([\d.]+)/); + // var silk = ua.match(/Silk\/([\d._]+)/); + // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/); + // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/); + // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/); + // var playbook = ua.match(/PlayBook/); + // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/); + var firefox = ua.match(/Firefox\/([\d.]+)/); + // var safari = webkit && ua.match(/Mobile\//) && !chrome; + // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome; + var ie = ua.match(/MSIE\s([\d.]+)/) + // IE 11 Trident/7.0; rv:11.0 + || ua.match(/Trident\/.+?rv:(([\d.]+))/); + var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+ + + // Todo: clean this up with a better OS/browser seperation: + // - discern (more) between multiple browsers on android + // - decide if kindle fire in silk mode is android or not + // - Firefox on Android doesn't specify the Android version + // - possibly devide in os, device and browser hashes + + // if (browser.webkit = !!webkit) browser.version = webkit[1]; + + // if (android) os.android = true, os.version = android[2]; + // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.'); + // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.'); + // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null; + // if (webos) os.webos = true, os.version = webos[2]; + // if (touchpad) os.touchpad = true; + // if (blackberry) os.blackberry = true, os.version = blackberry[2]; + // if (bb10) os.bb10 = true, os.version = bb10[2]; + // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2]; + // if (playbook) browser.playbook = true; + // if (kindle) os.kindle = true, os.version = kindle[1]; + // if (silk) browser.silk = true, browser.version = silk[1]; + // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true; + // if (chrome) browser.chrome = true, browser.version = chrome[1]; + if (firefox) browser.firefox = true, browser.version = firefox[1]; + // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true; + // if (webview) browser.webview = true; + if (ie) { + browser.ie = true; browser.version = ie[1]; + } + if (ie) { + browser.ie = true; + browser.version = ie[1]; + } + if (edge) { + browser.edge = true; + browser.version = edge[1]; + } + + // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || + // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/))); + // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || + // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || + // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/)))); + + return { + browser: browser, + os: os, + node: false, + // 原生canvas支持,改极端点了 + // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9) + canvasSupported : document.createElement('canvas').getContext ? true : false, + // @see + // works on most browsers + // IE10/11 does not support touch event, and MS Edge supports them but not by + // default, so we dont check navigator.maxTouchPoints for them here. + touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge, + // . + pointerEventsSupported: 'onpointerdown' in window + // Firefox supports pointer but not by default, + // only MS browsers are reliable on pointer events currently. + && (browser.edge || (browser.ie && browser.version >= 10)) + }; + } + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * ECharts global model + * + * @module {echarts/model/Global} + * + */ + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var Model = __webpack_require__(12); + var each = zrUtil.each; + var filter = zrUtil.filter; + var map = zrUtil.map; + var isArray = zrUtil.isArray; + var indexOf = zrUtil.indexOf; + var isObject = zrUtil.isObject; + + var ComponentModel = __webpack_require__(19); + + var globalDefault = __webpack_require__(23); + + var OPTION_INNER_KEY = '\0_ec_inner'; + + /** + * @alias module:echarts/model/Global + * + * @param {Object} option + * @param {module:echarts/model/Model} parentModel + * @param {Object} theme + */ + var GlobalModel = Model.extend({ + + constructor: GlobalModel, + + init: function (option, parentModel, theme, optionManager) { + theme = theme || {}; + + this.option = null; // Mark as not initialized. + + /** + * @type {module:echarts/model/Model} + * @private + */ + this._theme = new Model(theme); + + /** + * @type {module:echarts/model/OptionManager} + */ + this._optionManager = optionManager; + }, + + setOption: function (option, optionPreprocessorFuncs) { + zrUtil.assert( + !(OPTION_INNER_KEY in option), + 'please use chart.getOption()' + ); + + this._optionManager.setOption(option, optionPreprocessorFuncs); + + this.resetOption(); + }, + + /** + * @param {string} type null/undefined: reset all. + * 'recreate': force recreate all. + * 'timeline': only reset timeline option + * 'media': only reset media query option + * @return {boolean} Whether option changed. + */ + resetOption: function (type) { + var optionChanged = false; + var optionManager = this._optionManager; + + if (!type || type === 'recreate') { + var baseOption = optionManager.mountOption(type === 'recreate'); + + if (!this.option || type === 'recreate') { + initBase.call(this, baseOption); + } + else { + this.restoreData(); + this.mergeOption(baseOption); + } + optionChanged = true; + } + + if (type === 'timeline' || type === 'media') { + this.restoreData(); + } + + if (!type || type === 'recreate' || type === 'timeline') { + var timelineOption = optionManager.getTimelineOption(this); + timelineOption && (this.mergeOption(timelineOption), optionChanged = true); + } + + if (!type || type === 'recreate' || type === 'media') { + var mediaOptions = optionManager.getMediaOption(this, this._api); + if (mediaOptions.length) { + each(mediaOptions, function (mediaOption) { + this.mergeOption(mediaOption, optionChanged = true); + }, this); + } + } + + return optionChanged; + }, + + /** + * @protected + */ + mergeOption: function (newOption) { + var option = this.option; + var componentsMap = this._componentsMap; + var newCptTypes = []; + + // 如果不存在对应的 component model 则直接 merge + each(newOption, function (componentOption, mainType) { + if (componentOption == null) { + return; + } + + if (!ComponentModel.hasClass(mainType)) { + option[mainType] = option[mainType] == null + ? zrUtil.clone(componentOption) + : zrUtil.merge(option[mainType], componentOption, true); + } + else { + newCptTypes.push(mainType); + } + }); + + // FIXME OPTION 同步是否要改回原来的 + ComponentModel.topologicalTravel( + newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this + ); + + this._seriesIndices = this._seriesIndices || []; + + function visitComponent(mainType, dependencies) { + var newCptOptionList = modelUtil.normalizeToArray(newOption[mainType]); + + var mapResult = modelUtil.mappingToExists( + componentsMap[mainType], newCptOptionList + ); + + makeKeyInfo(mainType, mapResult); + + var dependentModels = getComponentsByTypes( + componentsMap, dependencies + ); + + option[mainType] = []; + componentsMap[mainType] = []; + + each(mapResult, function (resultItem, index) { + var componentModel = resultItem.exist; + var newCptOption = resultItem.option; + + zrUtil.assert( + isObject(newCptOption) || componentModel, + 'Empty component definition' + ); + + // Consider where is no new option and should be merged using {}, + // see removeEdgeAndAdd in topologicalTravel and + // ComponentModel.getAllClassMainTypes. + if (!newCptOption) { + componentModel.mergeOption({}, this); + componentModel.optionUpdated({}, false); + } + else { + var ComponentModelClass = ComponentModel.getClass( + mainType, resultItem.keyInfo.subType, true + ); + + if (componentModel && componentModel instanceof ComponentModelClass) { + componentModel.mergeOption(newCptOption, this); + componentModel.optionUpdated(newCptOption, false); + } + else { + // PENDING Global as parent ? + var extraOpt = zrUtil.extend( + { + dependentModels: dependentModels, + componentIndex: index + }, + resultItem.keyInfo + ); + componentModel = new ComponentModelClass( + newCptOption, this, this, extraOpt + ); + componentModel.init(newCptOption, this, this, extraOpt); + // Call optionUpdated after init. + // newCptOption has been used as componentModel.option + // and may be merged with theme and default, so pass null + // to avoid confusion. + componentModel.optionUpdated(null, true); + } + } + + componentsMap[mainType][index] = componentModel; + option[mainType][index] = componentModel.option; + }, this); + + // Backup series for filtering. + if (mainType === 'series') { + this._seriesIndices = createSeriesIndices(componentsMap.series); + } + } + }, + + /** + * Get option for output (cloned option and inner info removed) + * @public + * @return {Object} + */ + getOption: function () { + var option = zrUtil.clone(this.option); + + each(option, function (opts, mainType) { + if (ComponentModel.hasClass(mainType)) { + var opts = modelUtil.normalizeToArray(opts); + for (var i = opts.length - 1; i >= 0; i--) { + // Remove options with inner id. + if (modelUtil.isIdInner(opts[i])) { + opts.splice(i, 1); + } + } + option[mainType] = opts; + } + }); + + delete option[OPTION_INNER_KEY]; + + return option; + }, + + /** + * @return {module:echarts/model/Model} + */ + getTheme: function () { + return this._theme; + }, + + /** + * @param {string} mainType + * @param {number} [idx=0] + * @return {module:echarts/model/Component} + */ + getComponent: function (mainType, idx) { + var list = this._componentsMap[mainType]; + if (list) { + return list[idx || 0]; + } + }, + + /** + * @param {Object} condition + * @param {string} condition.mainType + * @param {string} [condition.subType] If ignore, only query by mainType + * @param {number} [condition.index] Either input index or id or name. + * @param {string} [condition.id] Either input index or id or name. + * @param {string} [condition.name] Either input index or id or name. + * @return {Array.} + */ + queryComponents: function (condition) { + var mainType = condition.mainType; + if (!mainType) { + return []; + } + + var index = condition.index; + var id = condition.id; + var name = condition.name; + + var cpts = this._componentsMap[mainType]; + + if (!cpts || !cpts.length) { + return []; + } + + var result; + + if (index != null) { + if (!isArray(index)) { + index = [index]; + } + result = filter(map(index, function (idx) { + return cpts[idx]; + }), function (val) { + return !!val; + }); + } + else if (id != null) { + var isIdArray = isArray(id); + result = filter(cpts, function (cpt) { + return (isIdArray && indexOf(id, cpt.id) >= 0) + || (!isIdArray && cpt.id === id); + }); + } + else if (name != null) { + var isNameArray = isArray(name); + result = filter(cpts, function (cpt) { + return (isNameArray && indexOf(name, cpt.name) >= 0) + || (!isNameArray && cpt.name === name); + }); + } + else { + // Return all components with mainType + result = cpts; + } + + return filterBySubType(result, condition); + }, + + /** + * The interface is different from queryComponents, + * which is convenient for inner usage. + * + * @usage + * var result = findComponents( + * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} + * ); + * var result = findComponents( + * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} + * ); + * var result = findComponents( + * {mainType: 'series'}, + * function (model, index) {...} + * ); + * // result like [component0, componnet1, ...] + * + * @param {Object} condition + * @param {string} condition.mainType Mandatory. + * @param {string} [condition.subType] Optional. + * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName}, + * where xxx is mainType. + * If query attribute is null/undefined or has no index/id/name, + * do not filtering by query conditions, which is convenient for + * no-payload situations or when target of action is global. + * @param {Function} [condition.filter] parameter: component, return boolean. + * @return {Array.} + */ + findComponents: function (condition) { + var query = condition.query; + var mainType = condition.mainType; + + var queryCond = getQueryCond(query); + var result = queryCond + ? this.queryComponents(queryCond) + : this._componentsMap[mainType]; + + return doFilter(filterBySubType(result, condition)); + + function getQueryCond(q) { + var indexAttr = mainType + 'Index'; + var idAttr = mainType + 'Id'; + var nameAttr = mainType + 'Name'; + return q && ( + q.hasOwnProperty(indexAttr) + || q.hasOwnProperty(idAttr) + || q.hasOwnProperty(nameAttr) + ) + ? { + mainType: mainType, + // subType will be filtered finally. + index: q[indexAttr], + id: q[idAttr], + name: q[nameAttr] + } + : null; + } + + function doFilter(res) { + return condition.filter + ? filter(res, condition.filter) + : res; + } + }, + + /** + * @usage + * eachComponent('legend', function (legendModel, index) { + * ... + * }); + * eachComponent(function (componentType, model, index) { + * // componentType does not include subType + * // (componentType is 'xxx' but not 'xxx.aa') + * }); + * eachComponent( + * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}, + * function (model, index) {...} + * ); + * eachComponent( + * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}, + * function (model, index) {...} + * ); + * + * @param {string|Object=} mainType When mainType is object, the definition + * is the same as the method 'findComponents'. + * @param {Function} cb + * @param {*} context + */ + eachComponent: function (mainType, cb, context) { + var componentsMap = this._componentsMap; + + if (typeof mainType === 'function') { + context = cb; + cb = mainType; + each(componentsMap, function (components, componentType) { + each(components, function (component, index) { + cb.call(context, componentType, component, index); + }); + }); + } + else if (zrUtil.isString(mainType)) { + each(componentsMap[mainType], cb, context); + } + else if (isObject(mainType)) { + var queryResult = this.findComponents(mainType); + each(queryResult, cb, context); + } + }, + + /** + * @param {string} name + * @return {Array.} + */ + getSeriesByName: function (name) { + var series = this._componentsMap.series; + return filter(series, function (oneSeries) { + return oneSeries.name === name; + }); + }, + + /** + * @param {number} seriesIndex + * @return {module:echarts/model/Series} + */ + getSeriesByIndex: function (seriesIndex) { + return this._componentsMap.series[seriesIndex]; + }, + + /** + * @param {string} subType + * @return {Array.} + */ + getSeriesByType: function (subType) { + var series = this._componentsMap.series; + return filter(series, function (oneSeries) { + return oneSeries.subType === subType; + }); + }, + + /** + * @return {Array.} + */ + getSeries: function () { + return this._componentsMap.series.slice(); + }, + + /** + * After filtering, series may be different + * frome raw series. + * + * @param {Function} cb + * @param {*} context + */ + eachSeries: function (cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.series[rawSeriesIndex]; + cb.call(context, series, rawSeriesIndex); + }, this); + }, + + /** + * Iterate raw series before filtered. + * + * @param {Function} cb + * @param {*} context + */ + eachRawSeries: function (cb, context) { + each(this._componentsMap.series, cb, context); + }, + + /** + * After filtering, series may be different. + * frome raw series. + * + * @parma {string} subType + * @param {Function} cb + * @param {*} context + */ + eachSeriesByType: function (subType, cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.series[rawSeriesIndex]; + if (series.subType === subType) { + cb.call(context, series, rawSeriesIndex); + } + }, this); + }, + + /** + * Iterate raw series before filtered of given type. + * + * @parma {string} subType + * @param {Function} cb + * @param {*} context + */ + eachRawSeriesByType: function (subType, cb, context) { + return each(this.getSeriesByType(subType), cb, context); + }, + + /** + * @param {module:echarts/model/Series} seriesModel + */ + isSeriesFiltered: function (seriesModel) { + assertSeriesInitialized(this); + return zrUtil.indexOf(this._seriesIndices, seriesModel.componentIndex) < 0; + }, + + /** + * @param {Function} cb + * @param {*} context + */ + filterSeries: function (cb, context) { + assertSeriesInitialized(this); + var filteredSeries = filter( + this._componentsMap.series, cb, context + ); + this._seriesIndices = createSeriesIndices(filteredSeries); + }, + + restoreData: function () { + var componentsMap = this._componentsMap; + + this._seriesIndices = createSeriesIndices(componentsMap.series); + + var componentTypes = []; + each(componentsMap, function (components, componentType) { + componentTypes.push(componentType); + }); + + ComponentModel.topologicalTravel( + componentTypes, + ComponentModel.getAllClassMainTypes(), + function (componentType, dependencies) { + each(componentsMap[componentType], function (component) { + component.restoreData(); + }); + } + ); + } + + }); + + /** + * @inner + */ + function mergeTheme(option, theme) { + for (var name in theme) { + // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理 + if (!ComponentModel.hasClass(name)) { + if (typeof theme[name] === 'object') { + option[name] = !option[name] + ? zrUtil.clone(theme[name]) + : zrUtil.merge(option[name], theme[name], false); + } + else { + if (option[name] == null) { + option[name] = theme[name]; + } + } + } + } + } + + function initBase(baseOption) { + baseOption = baseOption; + + // Using OPTION_INNER_KEY to mark that this option can not be used outside, + // i.e. `chart.setOption(chart.getModel().option);` is forbiden. + this.option = {}; + this.option[OPTION_INNER_KEY] = 1; + + /** + * @type {Object.>} + * @private + */ + this._componentsMap = {}; + + /** + * Mapping between filtered series list and raw series list. + * key: filtered series indices, value: raw series indices. + * @type {Array.} + * @private + */ + this._seriesIndices = null; + + mergeTheme(baseOption, this._theme.option); + + // TODO Needs clone when merging to the unexisted property + zrUtil.merge(baseOption, globalDefault, false); + + this.mergeOption(baseOption); + } + + /** + * @inner + * @param {Array.|string} types model types + * @return {Object} key: {string} type, value: {Array.} models + */ + function getComponentsByTypes(componentsMap, types) { + if (!zrUtil.isArray(types)) { + types = types ? [types] : []; + } + + var ret = {}; + each(types, function (type) { + ret[type] = (componentsMap[type] || []).slice(); + }); + + return ret; + } + + /** + * @inner + */ + function makeKeyInfo(mainType, mapResult) { + // We use this id to hash component models and view instances + // in echarts. id can be specified by user, or auto generated. + + // The id generation rule ensures new view instance are able + // to mapped to old instance when setOption are called in + // no-merge mode. So we generate model id by name and plus + // type in view id. + + // name can be duplicated among components, which is convenient + // to specify multi components (like series) by one name. + + // Ensure that each id is distinct. + var idMap = {}; + + each(mapResult, function (item, index) { + var existCpt = item.exist; + existCpt && (idMap[existCpt.id] = item); + }); + + each(mapResult, function (item, index) { + var opt = item.option; + + zrUtil.assert( + !opt || opt.id == null || !idMap[opt.id] || idMap[opt.id] === item, + 'id duplicates: ' + (opt && opt.id) + ); + + opt && opt.id != null && (idMap[opt.id] = item); + + // Complete subType + if (isObject(opt)) { + var subType = determineSubType(mainType, opt, item.exist); + item.keyInfo = {mainType: mainType, subType: subType}; + } + }); + + // Make name and id. + each(mapResult, function (item, index) { + var existCpt = item.exist; + var opt = item.option; + var keyInfo = item.keyInfo; + + if (!isObject(opt)) { + return; + } + + // name can be overwitten. Consider case: axis.name = '20km'. + // But id generated by name will not be changed, which affect + // only in that case: setOption with 'not merge mode' and view + // instance will be recreated, which can be accepted. + keyInfo.name = opt.name != null + ? opt.name + '' + : existCpt + ? existCpt.name + : '\0-'; + + if (existCpt) { + keyInfo.id = existCpt.id; + } + else if (opt.id != null) { + keyInfo.id = opt.id + ''; + } + else { + // Consider this situatoin: + // optionA: [{name: 'a'}, {name: 'a'}, {..}] + // optionB [{..}, {name: 'a'}, {name: 'a'}] + // Series with the same name between optionA and optionB + // should be mapped. + var idNum = 0; + do { + keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; + } + while (idMap[keyInfo.id]); + } + + idMap[keyInfo.id] = item; + }); + } + + /** + * @inner + */ + function determineSubType(mainType, newCptOption, existComponent) { + var subType = newCptOption.type + ? newCptOption.type + : existComponent + ? existComponent.subType + // Use determineSubType only when there is no existComponent. + : ComponentModel.determineSubType(mainType, newCptOption); + + // tooltip, markline, markpoint may always has no subType + return subType; + } + + /** + * @inner + */ + function createSeriesIndices(seriesModels) { + return map(seriesModels, function (series) { + return series.componentIndex; + }) || []; + } + + /** + * @inner + */ + function filterBySubType(components, condition) { + // Using hasOwnProperty for restrict. Consider + // subType is undefined in user payload. + return condition.hasOwnProperty('subType') + ? filter(components, function (cpt) { + return cpt.subType === condition.subType; + }) + : components; + } + + /** + * @inner + */ + function assertSeriesInitialized(ecModel) { + // Components that use _seriesIndices should depends on series component, + // which make sure that their initialization is after series. + if (true) { + if (!ecModel._seriesIndices) { + throw new Error('Series has not been initialized yet.'); + } + } + } + + zrUtil.mixin(GlobalModel, __webpack_require__(24)); + + module.exports = GlobalModel; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + /** + * @module zrender/core/util + */ + + + // 用于处理merge时无法遍历Date等对象的问题 + var BUILTIN_OBJECT = { + '[object Function]': 1, + '[object RegExp]': 1, + '[object Date]': 1, + '[object Error]': 1, + '[object CanvasGradient]': 1, + '[object CanvasPattern]': 1, + // In node-canvas Image can be Canvas.Image + '[object Image]': 1 + }; + + var objToString = Object.prototype.toString; + + var arrayProto = Array.prototype; + var nativeForEach = arrayProto.forEach; + var nativeFilter = arrayProto.filter; + var nativeSlice = arrayProto.slice; + var nativeMap = arrayProto.map; + var nativeReduce = arrayProto.reduce; + + /** + * @param {*} source + * @return {*} 拷贝后的新对象 + */ + function clone(source) { + if (typeof source == 'object' && source !== null) { + var result = source; + if (source instanceof Array) { + result = []; + for (var i = 0, len = source.length; i < len; i++) { + result[i] = clone(source[i]); + } + } + else if ( + !isBuildInObject(source) + // 是否为 dom 对象 + && !isDom(source) + ) { + result = {}; + for (var key in source) { + if (source.hasOwnProperty(key)) { + result[key] = clone(source[key]); + } + } + } + + return result; + } + + return source; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} target + * @param {*} source + * @param {boolean} [overwrite=false] + */ + function merge(target, source, overwrite) { + // We should escapse that source is string + // and enter for ... in ... + if (!isObject(source) || !isObject(target)) { + return overwrite ? clone(source) : target; + } + + for (var key in source) { + if (source.hasOwnProperty(key)) { + var targetProp = target[key]; + var sourceProp = source[key]; + + if (isObject(sourceProp) + && isObject(targetProp) + && !isArray(sourceProp) + && !isArray(targetProp) + && !isDom(sourceProp) + && !isDom(targetProp) + && !isBuildInObject(sourceProp) + && !isBuildInObject(targetProp) + ) { + // 如果需要递归覆盖,就递归调用merge + merge(targetProp, sourceProp, overwrite); + } + else if (overwrite || !(key in target)) { + // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况 + // NOTE,在 target[key] 不存在的时候也是直接覆盖 + target[key] = clone(source[key], true); + } + } + } + + return target; + } + + /** + * @param {Array} targetAndSources The first item is target, and the rests are source. + * @param {boolean} [overwrite=false] + * @return {*} target + */ + function mergeAll(targetAndSources, overwrite) { + var result = targetAndSources[0]; + for (var i = 1, len = targetAndSources.length; i < len; i++) { + result = merge(result, targetAndSources[i], overwrite); + } + return result; + } + + /** + * @param {*} target + * @param {*} source + * @memberOf module:zrender/core/util + */ + function extend(target, source) { + for (var key in source) { + if (source.hasOwnProperty(key)) { + target[key] = source[key]; + } + } + return target; + } + + /** + * @param {*} target + * @param {*} source + * @param {boolen} [overlay=false] + * @memberOf module:zrender/core/util + */ + function defaults(target, source, overlay) { + for (var key in source) { + if (source.hasOwnProperty(key) + && (overlay ? source[key] != null : target[key] == null) + ) { + target[key] = source[key]; + } + } + return target; + } + + function createCanvas() { + return document.createElement('canvas'); + } + // FIXME + var _ctx; + function getContext() { + if (!_ctx) { + // Use util.createCanvas instead of createCanvas + // because createCanvas may be overwritten in different environment + _ctx = util.createCanvas().getContext('2d'); + } + return _ctx; + } + + /** + * 查询数组中元素的index + * @memberOf module:zrender/core/util + */ + function indexOf(array, value) { + if (array) { + if (array.indexOf) { + return array.indexOf(value); + } + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + } + return -1; + } + + /** + * 构造类继承关系 + * + * @memberOf module:zrender/core/util + * @param {Function} clazz 源类 + * @param {Function} baseClazz 基类 + */ + function inherits(clazz, baseClazz) { + var clazzPrototype = clazz.prototype; + function F() {} + F.prototype = baseClazz.prototype; + clazz.prototype = new F(); + + for (var prop in clazzPrototype) { + clazz.prototype[prop] = clazzPrototype[prop]; + } + clazz.prototype.constructor = clazz; + clazz.superClass = baseClazz; + } + + /** + * @memberOf module:zrender/core/util + * @param {Object|Function} target + * @param {Object|Function} sorce + * @param {boolean} overlay + */ + function mixin(target, source, overlay) { + target = 'prototype' in target ? target.prototype : target; + source = 'prototype' in source ? source.prototype : source; + + defaults(target, source, overlay); + } + + /** + * @param {Array|TypedArray} data + */ + function isArrayLike(data) { + if (! data) { + return; + } + if (typeof data == 'string') { + return false; + } + return typeof data.length == 'number'; + } + + /** + * 数组或对象遍历 + * @memberOf module:zrender/core/util + * @param {Object|Array} obj + * @param {Function} cb + * @param {*} [context] + */ + function each(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.forEach && obj.forEach === nativeForEach) { + obj.forEach(cb, context); + } + else if (obj.length === +obj.length) { + for (var i = 0, len = obj.length; i < len; i++) { + cb.call(context, obj[i], i, obj); + } + } + else { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + cb.call(context, obj[key], key, obj); + } + } + } + } + + /** + * 数组映射 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function map(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.map && obj.map === nativeMap) { + return obj.map(cb, context); + } + else { + var result = []; + for (var i = 0, len = obj.length; i < len; i++) { + result.push(cb.call(context, obj[i], i, obj)); + } + return result; + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {Object} [memo] + * @param {*} [context] + * @return {Array} + */ + function reduce(obj, cb, memo, context) { + if (!(obj && cb)) { + return; + } + if (obj.reduce && obj.reduce === nativeReduce) { + return obj.reduce(cb, memo, context); + } + else { + for (var i = 0, len = obj.length; i < len; i++) { + memo = cb.call(context, memo, obj[i], i, obj); + } + return memo; + } + } + + /** + * 数组过滤 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function filter(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.filter && obj.filter === nativeFilter) { + return obj.filter(cb, context); + } + else { + var result = []; + for (var i = 0, len = obj.length; i < len; i++) { + if (cb.call(context, obj[i], i, obj)) { + result.push(obj[i]); + } + } + return result; + } + } + + /** + * 数组项查找 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function find(obj, cb, context) { + if (!(obj && cb)) { + return; + } + for (var i = 0, len = obj.length; i < len; i++) { + if (cb.call(context, obj[i], i, obj)) { + return obj[i]; + } + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Function} func + * @param {*} context + * @return {Function} + */ + function bind(func, context) { + var args = nativeSlice.call(arguments, 2); + return function () { + return func.apply(context, args.concat(nativeSlice.call(arguments))); + }; + } + + /** + * @memberOf module:zrender/core/util + * @param {Function} func + * @return {Function} + */ + function curry(func) { + var args = nativeSlice.call(arguments, 1); + return function () { + return func.apply(this, args.concat(nativeSlice.call(arguments))); + }; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isArray(value) { + return objToString.call(value) === '[object Array]'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isFunction(value) { + return typeof value === 'function'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isString(value) { + return objToString.call(value) === '[object String]'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return type === 'function' || (!!value && type == 'object'); + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isBuildInObject(value) { + return !!BUILTIN_OBJECT[objToString.call(value)]; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isDom(value) { + return value && value.nodeType === 1 + && typeof(value.nodeName) == 'string'; + } + + /** + * If value1 is not null, then return value1, otherwise judget rest of values. + * @memberOf module:zrender/core/util + * @return {*} Final value + */ + function retrieve(values) { + for (var i = 0, len = arguments.length; i < len; i++) { + if (arguments[i] != null) { + return arguments[i]; + } + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Array} arr + * @param {number} startIndex + * @param {number} endIndex + * @return {Array} + */ + function slice() { + return Function.call.apply(nativeSlice, arguments); + } + + /** + * @memberOf module:zrender/core/util + * @param {boolean} condition + * @param {string} message + */ + function assert(condition, message) { + if (!condition) { + throw new Error(message); + } + } + + var util = { + inherits: inherits, + mixin: mixin, + clone: clone, + merge: merge, + mergeAll: mergeAll, + extend: extend, + defaults: defaults, + getContext: getContext, + createCanvas: createCanvas, + indexOf: indexOf, + slice: slice, + find: find, + isArrayLike: isArrayLike, + each: each, + map: map, + reduce: reduce, + filter: filter, + bind: bind, + curry: curry, + isArray: isArray, + isString: isString, + isObject: isObject, + isFunction: isFunction, + isBuildInObject: isBuildInObject, + isDom: isDom, + retrieve: retrieve, + assert: assert, + noop: function () {} + }; + module.exports = util; + + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + + + var formatUtil = __webpack_require__(6); + var nubmerUtil = __webpack_require__(7); + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var modelUtil = {}; + + /** + * If value is not array, then translate it to array. + * @param {*} value + * @return {Array} [value] or value + */ + modelUtil.normalizeToArray = function (value) { + return value instanceof Array + ? value + : value == null + ? [] + : [value]; + }; + + /** + * Sync default option between normal and emphasis like `position` and `show` + * In case some one will write code like + * label: { + * normal: { + * show: false, + * position: 'outside', + * textStyle: { + * fontSize: 18 + * } + * }, + * emphasis: { + * show: true + * } + * } + * @param {Object} opt + * @param {Array.} subOpts + */ + modelUtil.defaultEmphasis = function (opt, subOpts) { + if (opt) { + var emphasisOpt = opt.emphasis = opt.emphasis || {}; + var normalOpt = opt.normal = opt.normal || {}; + + // Default emphasis option from normal + zrUtil.each(subOpts, function (subOptName) { + var val = zrUtil.retrieve(emphasisOpt[subOptName], normalOpt[subOptName]); + if (val != null) { + emphasisOpt[subOptName] = val; + } + }); + } + }; + + modelUtil.LABEL_OPTIONS = ['position', 'show', 'textStyle', 'distance', 'formatter']; + + /** + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method retieves value from data. + * @param {string|number|Date|Array|Object} dataItem + * @return {number|string|Date|Array.} + */ + modelUtil.getDataItemValue = function (dataItem) { + // Performance sensitive. + return dataItem && (dataItem.value == null ? dataItem : dataItem.value); + }; + + /** + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method determine if dataItem has extra option besides value + * @param {string|number|Date|Array|Object} dataItem + */ + modelUtil.isDataItemOption = function (dataItem) { + return zrUtil.isObject(dataItem) + && !(dataItem instanceof Array); + // // markLine data can be array + // && !(dataItem[0] && zrUtil.isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); + }; + + /** + * This helper method convert value in data. + * @param {string|number|Date} value + * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'. + */ + modelUtil.converDataValue = function (value, dimInfo) { + // Performance sensitive. + var dimType = dimInfo && dimInfo.type; + if (dimType === 'ordinal') { + return value; + } + + if (dimType === 'time' && !isFinite(value) && value != null && value !== '-') { + value = +nubmerUtil.parseDate(value); + } + + // dimType defaults 'number'. + // If dimType is not ordinal and value is null or undefined or NaN or '-', + // parse to NaN. + return (value == null || value === '') + ? NaN : +value; // If string (like '-'), using '+' parse to NaN + }; + + /** + * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data. + * @param {module:echarts/data/List} data + * @param {Object} opt + * @param {string} [opt.seriesIndex] + * @param {Object} [opt.name] + * @param {Object} [opt.mainType] + * @param {Object} [opt.subType] + */ + modelUtil.createDataFormatModel = function (data, opt) { + var model = new Model(); + zrUtil.mixin(model, modelUtil.dataFormatMixin); + model.seriesIndex = opt.seriesIndex; + model.name = opt.name || ''; + model.mainType = opt.mainType; + model.subType = opt.subType; + + model.getData = function () { + return data; + }; + return model; + }; + + // PENDING A little ugly + modelUtil.dataFormatMixin = { + /** + * Get params for formatter + * @param {number} dataIndex + * @param {string} [dataType] + * @return {Object} + */ + getDataParams: function (dataIndex, dataType) { + var data = this.getData(dataType); + + var seriesIndex = this.seriesIndex; + var seriesName = this.name; + + var rawValue = this.getRawValue(dataIndex, dataType); + var rawDataIndex = data.getRawIndex(dataIndex); + var name = data.getName(dataIndex, true); + var itemOpt = data.getRawDataItem(dataIndex); + + return { + componentType: this.mainType, + componentSubType: this.subType, + seriesType: this.mainType === 'series' ? this.subType : null, + seriesIndex: seriesIndex, + seriesName: seriesName, + name: name, + dataIndex: rawDataIndex, + data: itemOpt, + dataType: dataType, + value: rawValue, + color: data.getItemVisual(dataIndex, 'color'), + + // Param name list for mapping `a`, `b`, `c`, `d`, `e` + $vars: ['seriesName', 'name', 'value'] + }; + }, + + /** + * Format label + * @param {number} dataIndex + * @param {string} [status='normal'] 'normal' or 'emphasis' + * @param {string} [dataType] + * @param {number} [dimIndex] + * @return {string} + */ + getFormattedLabel: function (dataIndex, status, dataType, dimIndex) { + status = status || 'normal'; + var data = this.getData(dataType); + var itemModel = data.getItemModel(dataIndex); + + var params = this.getDataParams(dataIndex, dataType); + if (dimIndex != null && (params.value instanceof Array)) { + params.value = params.value[dimIndex]; + } + + var formatter = itemModel.get(['label', status, 'formatter']); + + if (typeof formatter === 'function') { + params.status = status; + return formatter(params); + } + else if (typeof formatter === 'string') { + return formatUtil.formatTpl(formatter, params); + } + }, + + /** + * Get raw value in option + * @param {number} idx + * @param {string} [dataType] + * @return {Object} + */ + getRawValue: function (idx, dataType) { + var data = this.getData(dataType); + var dataItem = data.getRawDataItem(idx); + if (dataItem != null) { + return (zrUtil.isObject(dataItem) && !(dataItem instanceof Array)) + ? dataItem.value : dataItem; + } + }, + + /** + * Should be implemented. + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {number} [dataType] + * @return {string} tooltip string + */ + formatTooltip: zrUtil.noop + }; + + /** + * Mapping to exists for merge. + * + * @public + * @param {Array.|Array.} exists + * @param {Object|Array.} newCptOptions + * @return {Array.} Result, like [{exist: ..., option: ...}, {}], + * which order is the same as exists. + */ + modelUtil.mappingToExists = function (exists, newCptOptions) { + // Mapping by the order by original option (but not order of + // new option) in merge mode. Because we should ensure + // some specified index (like xAxisIndex) is consistent with + // original option, which is easy to understand, espatially in + // media query. And in most case, merge option is used to + // update partial option but not be expected to change order. + newCptOptions = (newCptOptions || []).slice(); + + var result = zrUtil.map(exists || [], function (obj, index) { + return {exist: obj}; + }); + + // Mapping by id or name if specified. + zrUtil.each(newCptOptions, function (cptOption, index) { + if (!zrUtil.isObject(cptOption)) { + return; + } + + // id has highest priority. + for (var i = 0; i < result.length; i++) { + if (!result[i].option // Consider name: two map to one. + && cptOption.id != null + && result[i].exist.id === cptOption.id + '' + ) { + result[i].option = cptOption; + newCptOptions[index] = null; + return; + } + } + + for (var i = 0; i < result.length; i++) { + var exist = result[i].exist; + if (!result[i].option // Consider name: two map to one. + // Can not match when both ids exist but different. + && (exist.id == null || cptOption.id == null) + && cptOption.name != null + && !modelUtil.isIdInner(cptOption) + && !modelUtil.isIdInner(exist) + && exist.name === cptOption.name + '' + ) { + result[i].option = cptOption; + newCptOptions[index] = null; + return; + } + } + }); + + // Otherwise mapping by index. + zrUtil.each(newCptOptions, function (cptOption, index) { + if (!zrUtil.isObject(cptOption)) { + return; + } + + var i = 0; + for (; i < result.length; i++) { + var exist = result[i].exist; + if (!result[i].option + && !modelUtil.isIdInner(exist) + // Caution: + // Do not overwrite id. But name can be overwritten, + // because axis use name as 'show label text'. + // 'exist' always has id and name and we dont + // need to check it. + && cptOption.id == null + ) { + result[i].option = cptOption; + break; + } + } + + if (i >= result.length) { + result.push({option: cptOption}); + } + }); + + return result; + }; + + /** + * @public + * @param {Object} cptOption + * @return {boolean} + */ + modelUtil.isIdInner = function (cptOption) { + return zrUtil.isObject(cptOption) + && cptOption.id + && (cptOption.id + '').indexOf('\0_ec_\0') === 0; + }; + + /** + * A helper for removing duplicate items between batchA and batchB, + * and in themselves, and categorize by series. + * + * @param {Array.} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @param {Array.} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @return {Array., Array.>} result: [resultBatchA, resultBatchB] + */ + modelUtil.compressBatches = function (batchA, batchB) { + var mapA = {}; + var mapB = {}; + + makeMap(batchA || [], mapA); + makeMap(batchB || [], mapB, mapA); + + return [mapToArray(mapA), mapToArray(mapB)]; + + function makeMap(sourceBatch, map, otherMap) { + for (var i = 0, len = sourceBatch.length; i < len; i++) { + var seriesId = sourceBatch[i].seriesId; + var dataIndices = modelUtil.normalizeToArray(sourceBatch[i].dataIndex); + var otherDataIndices = otherMap && otherMap[seriesId]; + + for (var j = 0, lenj = dataIndices.length; j < lenj; j++) { + var dataIndex = dataIndices[j]; + + if (otherDataIndices && otherDataIndices[dataIndex]) { + otherDataIndices[dataIndex] = null; + } + else { + (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1; + } + } + } + } + + function mapToArray(map, isData) { + var result = []; + for (var i in map) { + if (map.hasOwnProperty(i) && map[i] != null) { + if (isData) { + result.push(+i); + } + else { + var dataIndices = mapToArray(map[i], true); + dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices}); + } + } + } + return result; + } + }; + + module.exports = modelUtil; + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var textContain = __webpack_require__(8); + + var formatUtil = {}; + /** + * 每三位默认加,格式化 + * @type {string|number} x + */ + formatUtil.addCommas = function (x) { + if (isNaN(x)) { + return '-'; + } + x = (x + '').split('.'); + return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,') + + (x.length > 1 ? ('.' + x[1]) : ''); + }; + + /** + * @param {string} str + * @return {string} str + */ + formatUtil.toCamelCase = function (str) { + return str.toLowerCase().replace(/-(.)/g, function(match, group1) { + return group1.toUpperCase(); + }); + }; + + /** + * Normalize css liked array configuration + * e.g. + * 3 => [3, 3, 3, 3] + * [4, 2] => [4, 2, 4, 2] + * [4, 3, 2] => [4, 3, 2, 3] + * @param {number|Array.} val + */ + formatUtil.normalizeCssArray = function (val) { + var len = val.length; + if (typeof (val) === 'number') { + return [val, val, val, val]; + } + else if (len === 2) { + // vertical | horizontal + return [val[0], val[1], val[0], val[1]]; + } + else if (len === 3) { + // top | horizontal | bottom + return [val[0], val[1], val[2], val[1]]; + } + return val; + }; + + formatUtil.encodeHTML = function (source) { + return String(source) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }; + + var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + + var wrapVar = function (varName, seriesIdx) { + return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; + }; + + /** + * Template formatter + * @param {string} tpl + * @param {Array.|Object} paramsList + * @return {string} + */ + formatUtil.formatTpl = function (tpl, paramsList) { + if (!zrUtil.isArray(paramsList)) { + paramsList = [paramsList]; + } + var seriesLen = paramsList.length; + if (!seriesLen) { + return ''; + } + + var $vars = paramsList[0].$vars || []; + for (var i = 0; i < $vars.length; i++) { + var alias = TPL_VAR_ALIAS[i]; + tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0)); + } + for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) { + for (var k = 0; k < $vars.length; k++) { + tpl = tpl.replace( + wrapVar(TPL_VAR_ALIAS[k], seriesIdx), + paramsList[seriesIdx][$vars[k]] + ); + } + } + + return tpl; + }; + + + /** + * @param {string} str + * @return {string} + * @inner + */ + var s2d = function (str) { + return str < 10 ? ('0' + str) : str; + }; + + /** + * ISO Date format + * @param {string} tpl + * @param {number} value + * @inner + */ + formatUtil.formatTime = function (tpl, value) { + if (tpl === 'week' + || tpl === 'month' + || tpl === 'quarter' + || tpl === 'half-year' + || tpl === 'year' + ) { + tpl = 'MM-dd\nyyyy'; + } + + var date = numberUtil.parseDate(value); + var y = date.getFullYear(); + var M = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var m = date.getMinutes(); + var s = date.getSeconds(); + + tpl = tpl.replace('MM', s2d(M)) + .toLowerCase() + .replace('yyyy', y) + .replace('yy', y % 100) + .replace('dd', s2d(d)) + .replace('d', d) + .replace('hh', s2d(h)) + .replace('h', h) + .replace('mm', s2d(m)) + .replace('m', m) + .replace('ss', s2d(s)) + .replace('s', s); + + return tpl; + }; + + /** + * Capital first + * @param {string} str + * @return {string} + */ + formatUtil.capitalFirst = function (str) { + return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; + }; + + formatUtil.truncateText = textContain.truncateText; + + module.exports = formatUtil; + + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + /** + * 数值处理模块 + * @module echarts/util/number + */ + + + + var number = {}; + + var RADIAN_EPSILON = 1e-4; + + function _trim(str) { + return str.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + /** + * Linear mapping a value from domain to range + * @memberOf module:echarts/util/number + * @param {(number|Array.)} val + * @param {Array.} domain Domain extent domain[0] can be bigger than domain[1] + * @param {Array.} range Range extent range[0] can be bigger than range[1] + * @param {boolean} clamp + * @return {(number|Array.} + */ + number.linearMap = function (val, domain, range, clamp) { + var subDomain = domain[1] - domain[0]; + var subRange = range[1] - range[0]; + + if (subDomain === 0) { + return subRange === 0 + ? range[0] + : (range[0] + range[1]) / 2; + } + + // Avoid accuracy problem in edge, such as + // 146.39 - 62.83 === 83.55999999999999. + // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError + // It is a little verbose for efficiency considering this method + // is a hotspot. + if (clamp) { + if (subDomain > 0) { + if (val <= domain[0]) { + return range[0]; + } + else if (val >= domain[1]) { + return range[1]; + } + } + else { + if (val >= domain[0]) { + return range[0]; + } + else if (val <= domain[1]) { + return range[1]; + } + } + } + else { + if (val === domain[0]) { + return range[0]; + } + if (val === domain[1]) { + return range[1]; + } + } + + return (val - domain[0]) / subDomain * subRange + range[0]; + }; + + /** + * Convert a percent string to absolute number. + * Returns NaN if percent is not a valid string or number + * @memberOf module:echarts/util/number + * @param {string|number} percent + * @param {number} all + * @return {number} + */ + number.parsePercent = function(percent, all) { + switch (percent) { + case 'center': + case 'middle': + percent = '50%'; + break; + case 'left': + case 'top': + percent = '0%'; + break; + case 'right': + case 'bottom': + percent = '100%'; + break; + } + if (typeof percent === 'string') { + if (_trim(percent).match(/%$/)) { + return parseFloat(percent) / 100 * all; + } + + return parseFloat(percent); + } + + return percent == null ? NaN : +percent; + }; + + /** + * Fix rounding error of float numbers + * @param {number} x + * @return {number} + */ + number.round = function (x, precision) { + if (precision == null) { + precision = 10; + } + // PENDING + return +(+x).toFixed(precision); + }; + + number.asc = function (arr) { + arr.sort(function (a, b) { + return a - b; + }); + return arr; + }; + + /** + * Get precision + * @param {number} val + */ + number.getPrecision = function (val) { + val = +val; + if (isNaN(val)) { + return 0; + } + // It is much faster than methods converting number to string as follows + // var tmp = val.toString(); + // return tmp.length - 1 - tmp.indexOf('.'); + // especially when precision is low + var e = 1; + var count = 0; + while (Math.round(val * e) / e !== val) { + e *= 10; + count++; + } + return count; + }; + + number.getPrecisionSafe = function (val) { + var str = val.toString(); + var dotIndex = str.indexOf('.'); + if (dotIndex < 0) { + return 0; + } + return str.length - 1 - dotIndex; + }; + + /** + * @param {Array.} dataExtent + * @param {Array.} pixelExtent + * @return {number} precision + */ + number.getPixelPrecision = function (dataExtent, pixelExtent) { + var log = Math.log; + var LN10 = Math.LN10; + var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10); + var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); + return Math.max( + -dataQuantity + sizeQuantity, + 0 + ); + }; + + // Number.MAX_SAFE_INTEGER, ie do not support. + number.MAX_SAFE_INTEGER = 9007199254740991; + + /** + * To 0 - 2 * PI, considering negative radian. + * @param {number} radian + * @return {number} + */ + number.remRadian = function (radian) { + var pi2 = Math.PI * 2; + return (radian % pi2 + pi2) % pi2; + }; + + /** + * @param {type} radian + * @return {boolean} + */ + number.isRadianAroundZero = function (val) { + return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; + }; + + /** + * @param {string|Date|number} value + * @return {Date} date + */ + number.parseDate = function (value) { + if (value instanceof Date) { + return value; + } + else if (typeof value === 'string') { + // Treat as ISO format. See issue #3623 + var ret = new Date(value); + if (isNaN(+ret)) { + // FIXME new Date('1970-01-01') is UTC, new Date('1970/01/01') is local + ret = new Date(new Date(value.replace(/-/g, '/')) - new Date('1970/01/01')); + } + return ret; + } + + return new Date(Math.round(value)); + }; + + /** + * Quantity of a number. e.g. 0.1, 1, 10, 100 + * @param {number} val + * @return {number} + */ + number.quantity = function (val) { + return Math.pow(10, Math.floor(Math.log(val) / Math.LN10)); + }; + + // "Nice Numbers for Graph Labels" of Graphic Gems + /** + * find a “nice” number approximately equal to x. Round the number if round = true, take ceiling if round = false + * The primary observation is that the “nicest” numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers. + * @param {number} val + * @param {boolean} round + * @return {number} + */ + number.nice = function (val, round) { + var exp10 = number.quantity(val); + var f = val / exp10; // between 1 and 10 + var nf; + if (round) { + if (f < 1.5) { nf = 1; } + else if (f < 2.5) { nf = 2; } + else if (f < 4) { nf = 3; } + else if (f < 7) { nf = 5; } + else { nf = 10; } + } + else { + if (f < 1) { nf = 1; } + else if (f < 2) { nf = 2; } + else if (f < 3) { nf = 3; } + else if (f < 5) { nf = 5; } + else { nf = 10; } + } + return nf * exp10; + }; + + module.exports = number; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + + + var textWidthCache = {}; + var textWidthCacheCounter = 0; + var TEXT_CACHE_MAX = 5000; + + var util = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var retrieve = util.retrieve; + + function getTextWidth(text, textFont) { + var key = text + ':' + textFont; + if (textWidthCache[key]) { + return textWidthCache[key]; + } + + var textLines = (text + '').split('\n'); + var width = 0; + + for (var i = 0, l = textLines.length; i < l; i++) { + // measureText 可以被覆盖以兼容不支持 Canvas 的环境 + width = Math.max(textContain.measureText(textLines[i], textFont).width, width); + } + + if (textWidthCacheCounter > TEXT_CACHE_MAX) { + textWidthCacheCounter = 0; + textWidthCache = {}; + } + textWidthCacheCounter++; + textWidthCache[key] = width; + + return width; + } + + function getTextRect(text, textFont, textAlign, textBaseline) { + var textLineLen = ((text || '') + '').split('\n').length; + + var width = getTextWidth(text, textFont); + // FIXME 高度计算比较粗暴 + var lineHeight = getTextWidth('国', textFont); + var height = textLineLen * lineHeight; + + var rect = new BoundingRect(0, 0, width, height); + // Text has a special line height property + rect.lineHeight = lineHeight; + + switch (textBaseline) { + case 'bottom': + case 'alphabetic': + rect.y -= lineHeight; + break; + case 'middle': + rect.y -= lineHeight / 2; + break; + // case 'hanging': + // case 'top': + } + + // FIXME Right to left language + switch (textAlign) { + case 'end': + case 'right': + rect.x -= rect.width; + break; + case 'center': + rect.x -= rect.width / 2; + break; + // case 'start': + // case 'left': + } + + return rect; + } + + function adjustTextPositionOnRect(textPosition, rect, textRect, distance) { + + var x = rect.x; + var y = rect.y; + + var height = rect.height; + var width = rect.width; + + var textHeight = textRect.height; + + var halfHeight = height / 2 - textHeight / 2; + + var textAlign = 'left'; + + switch (textPosition) { + case 'left': + x -= distance; + y += halfHeight; + textAlign = 'right'; + break; + case 'right': + x += distance + width; + y += halfHeight; + textAlign = 'left'; + break; + case 'top': + x += width / 2; + y -= distance + textHeight; + textAlign = 'center'; + break; + case 'bottom': + x += width / 2; + y += height + distance; + textAlign = 'center'; + break; + case 'inside': + x += width / 2; + y += halfHeight; + textAlign = 'center'; + break; + case 'insideLeft': + x += distance; + y += halfHeight; + textAlign = 'left'; + break; + case 'insideRight': + x += width - distance; + y += halfHeight; + textAlign = 'right'; + break; + case 'insideTop': + x += width / 2; + y += distance; + textAlign = 'center'; + break; + case 'insideBottom': + x += width / 2; + y += height - textHeight - distance; + textAlign = 'center'; + break; + case 'insideTopLeft': + x += distance; + y += distance; + textAlign = 'left'; + break; + case 'insideTopRight': + x += width - distance; + y += distance; + textAlign = 'right'; + break; + case 'insideBottomLeft': + x += distance; + y += height - textHeight - distance; + break; + case 'insideBottomRight': + x += width - distance; + y += height - textHeight - distance; + textAlign = 'right'; + break; + } + + return { + x: x, + y: y, + textAlign: textAlign, + textBaseline: 'top' + }; + } + + /** + * Show ellipsis if overflow. + * + * @param {string} text + * @param {string} containerWidth + * @param {string} textFont + * @param {number} [ellipsis='...'] + * @param {Object} [options] + * @param {number} [options.maxIterations=3] + * @param {number} [options.minChar=0] If truncate result are less + * then minChar, ellipsis will not show, which is + * better for user hint in some cases. + * @param {number} [options.placeholder=''] When all truncated, use the placeholder. + * @return {string} + */ + function truncateText(text, containerWidth, textFont, ellipsis, options) { + if (!containerWidth) { + return ''; + } + + options = options || {}; + + ellipsis = retrieve(ellipsis, '...'); + var maxIterations = retrieve(options.maxIterations, 2); + var minChar = retrieve(options.minChar, 0); + // FIXME + // Other languages? + var cnCharWidth = getTextWidth('国', textFont); + // FIXME + // Consider proportional font? + var ascCharWidth = getTextWidth('a', textFont); + var placeholder = retrieve(options.placeholder, ''); + + // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'. + // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'. + var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap. + for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) { + contentWidth -= ascCharWidth; + } + + var ellipsisWidth = getTextWidth(ellipsis); + if (ellipsisWidth > contentWidth) { + ellipsis = ''; + ellipsisWidth = 0; + } + + contentWidth = containerWidth - ellipsisWidth; + + var textLines = (text + '').split('\n'); + + for (var i = 0, len = textLines.length; i < len; i++) { + var textLine = textLines[i]; + var lineWidth = getTextWidth(textLine, textFont); + + if (lineWidth <= containerWidth) { + continue; + } + + for (var j = 0;; j++) { + if (lineWidth <= contentWidth || j >= maxIterations) { + textLine += ellipsis; + break; + } + + var subLength = j === 0 + ? estimateLength(textLine, contentWidth, ascCharWidth, cnCharWidth) + : lineWidth > 0 + ? Math.floor(textLine.length * contentWidth / lineWidth) + : 0; + + textLine = textLine.substr(0, subLength); + lineWidth = getTextWidth(textLine, textFont); + } + + if (textLine === '') { + textLine = placeholder; + } + + textLines[i] = textLine; + } + + return textLines.join('\n'); + } + + function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) { + var width = 0; + var i = 0; + for (var len = text.length; i < len && width < contentWidth; i++) { + var charCode = text.charCodeAt(i); + width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth; + } + return i; + } + + var textContain = { + + getWidth: getTextWidth, + + getBoundingRect: getTextRect, + + adjustTextPositionOnRect: adjustTextPositionOnRect, + + truncateText: truncateText, + + measureText: function (text, textFont) { + var ctx = util.getContext(); + ctx.font = textFont || '12px sans-serif'; + return ctx.measureText(text); + } + }; + + module.exports = textContain; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module echarts/core/BoundingRect + */ + + + var vec2 = __webpack_require__(10); + var matrix = __webpack_require__(11); + + var v2ApplyTransform = vec2.applyTransform; + var mathMin = Math.min; + var mathAbs = Math.abs; + var mathMax = Math.max; + /** + * @alias module:echarts/core/BoundingRect + */ + function BoundingRect(x, y, width, height) { + /** + * @type {number} + */ + this.x = x; + /** + * @type {number} + */ + this.y = y; + /** + * @type {number} + */ + this.width = width; + /** + * @type {number} + */ + this.height = height; + } + + BoundingRect.prototype = { + + constructor: BoundingRect, + + /** + * @param {module:echarts/core/BoundingRect} other + */ + union: function (other) { + var x = mathMin(other.x, this.x); + var y = mathMin(other.y, this.y); + + this.width = mathMax( + other.x + other.width, + this.x + this.width + ) - x; + this.height = mathMax( + other.y + other.height, + this.y + this.height + ) - y; + this.x = x; + this.y = y; + }, + + /** + * @param {Array.} m + * @methods + */ + applyTransform: (function () { + var min = []; + var max = []; + return function (m) { + // In case usage like this + // el.getBoundingRect().applyTransform(el.transform) + // And element has no transform + if (!m) { + return; + } + min[0] = this.x; + min[1] = this.y; + max[0] = this.x + this.width; + max[1] = this.y + this.height; + + v2ApplyTransform(min, min, m); + v2ApplyTransform(max, max, m); + + this.x = mathMin(min[0], max[0]); + this.y = mathMin(min[1], max[1]); + this.width = mathAbs(max[0] - min[0]); + this.height = mathAbs(max[1] - min[1]); + }; + })(), + + /** + * Calculate matrix of transforming from self to target rect + * @param {module:zrender/core/BoundingRect} b + * @return {Array.} + */ + calculateTransform: function (b) { + var a = this; + var sx = b.width / a.width; + var sy = b.height / a.height; + + var m = matrix.create(); + + // 矩阵右乘 + matrix.translate(m, m, [-a.x, -a.y]); + matrix.scale(m, m, [sx, sy]); + matrix.translate(m, m, [b.x, b.y]); + + return m; + }, + + /** + * @param {(module:echarts/core/BoundingRect|Object)} b + * @return {boolean} + */ + intersect: function (b) { + var a = this; + var ax0 = a.x; + var ax1 = a.x + a.width; + var ay0 = a.y; + var ay1 = a.y + a.height; + + var bx0 = b.x; + var bx1 = b.x + b.width; + var by0 = b.y; + var by1 = b.y + b.height; + + return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); + }, + + contain: function (x, y) { + var rect = this; + return x >= rect.x + && x <= (rect.x + rect.width) + && y >= rect.y + && y <= (rect.y + rect.height); + }, + + /** + * @return {module:echarts/core/BoundingRect} + */ + clone: function () { + return new BoundingRect(this.x, this.y, this.width, this.height); + }, + + /** + * Copy from another rect + */ + copy: function (other) { + this.x = other.x; + this.y = other.y; + this.width = other.width; + this.height = other.height; + } + }; + + module.exports = BoundingRect; + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + + var ArrayCtor = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + + /** + * @typedef {Float32Array|Array.} Vector2 + */ + /** + * 二维向量类 + * @exports zrender/tool/vector + */ + var vector = { + /** + * 创建一个向量 + * @param {number} [x=0] + * @param {number} [y=0] + * @return {Vector2} + */ + create: function (x, y) { + var out = new ArrayCtor(2); + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + out[0] = x; + out[1] = y; + return out; + }, + + /** + * 复制向量数据 + * @param {Vector2} out + * @param {Vector2} v + * @return {Vector2} + */ + copy: function (out, v) { + out[0] = v[0]; + out[1] = v[1]; + return out; + }, + + /** + * 克隆一个向量 + * @param {Vector2} v + * @return {Vector2} + */ + clone: function (v) { + var out = new ArrayCtor(2); + out[0] = v[0]; + out[1] = v[1]; + return out; + }, + + /** + * 设置向量的两个项 + * @param {Vector2} out + * @param {number} a + * @param {number} b + * @return {Vector2} 结果 + */ + set: function (out, a, b) { + out[0] = a; + out[1] = b; + return out; + }, + + /** + * 向量相加 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + add: function (out, v1, v2) { + out[0] = v1[0] + v2[0]; + out[1] = v1[1] + v2[1]; + return out; + }, + + /** + * 向量缩放后相加 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + * @param {number} a + */ + scaleAndAdd: function (out, v1, v2, a) { + out[0] = v1[0] + v2[0] * a; + out[1] = v1[1] + v2[1] * a; + return out; + }, + + /** + * 向量相减 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + sub: function (out, v1, v2) { + out[0] = v1[0] - v2[0]; + out[1] = v1[1] - v2[1]; + return out; + }, + + /** + * 向量长度 + * @param {Vector2} v + * @return {number} + */ + len: function (v) { + return Math.sqrt(this.lenSquare(v)); + }, + + /** + * 向量长度平方 + * @param {Vector2} v + * @return {number} + */ + lenSquare: function (v) { + return v[0] * v[0] + v[1] * v[1]; + }, + + /** + * 向量乘法 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + mul: function (out, v1, v2) { + out[0] = v1[0] * v2[0]; + out[1] = v1[1] * v2[1]; + return out; + }, + + /** + * 向量除法 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + div: function (out, v1, v2) { + out[0] = v1[0] / v2[0]; + out[1] = v1[1] / v2[1]; + return out; + }, + + /** + * 向量点乘 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + dot: function (v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1]; + }, + + /** + * 向量缩放 + * @param {Vector2} out + * @param {Vector2} v + * @param {number} s + */ + scale: function (out, v, s) { + out[0] = v[0] * s; + out[1] = v[1] * s; + return out; + }, + + /** + * 向量归一化 + * @param {Vector2} out + * @param {Vector2} v + */ + normalize: function (out, v) { + var d = vector.len(v); + if (d === 0) { + out[0] = 0; + out[1] = 0; + } + else { + out[0] = v[0] / d; + out[1] = v[1] / d; + } + return out; + }, + + /** + * 计算向量间距离 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + distance: function (v1, v2) { + return Math.sqrt( + (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]) + ); + }, + + /** + * 向量距离平方 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + distanceSquare: function (v1, v2) { + return (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]); + }, + + /** + * 求负向量 + * @param {Vector2} out + * @param {Vector2} v + */ + negate: function (out, v) { + out[0] = -v[0]; + out[1] = -v[1]; + return out; + }, + + /** + * 插值两个点 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + * @param {number} t + */ + lerp: function (out, v1, v2, t) { + out[0] = v1[0] + t * (v2[0] - v1[0]); + out[1] = v1[1] + t * (v2[1] - v1[1]); + return out; + }, + + /** + * 矩阵左乘向量 + * @param {Vector2} out + * @param {Vector2} v + * @param {Vector2} m + */ + applyTransform: function (out, v, m) { + var x = v[0]; + var y = v[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + }, + /** + * 求两个向量最小值 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + min: function (out, v1, v2) { + out[0] = Math.min(v1[0], v2[0]); + out[1] = Math.min(v1[1], v2[1]); + return out; + }, + /** + * 求两个向量最大值 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + max: function (out, v1, v2) { + out[0] = Math.max(v1[0], v2[0]); + out[1] = Math.max(v1[1], v2[1]); + return out; + } + }; + + vector.length = vector.len; + vector.lengthSquare = vector.lenSquare; + vector.dist = vector.distance; + vector.distSquare = vector.distanceSquare; + + module.exports = vector; + + + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + + var ArrayCtor = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + /** + * 3x2矩阵操作类 + * @exports zrender/tool/matrix + */ + var matrix = { + /** + * 创建一个单位矩阵 + * @return {Float32Array|Array.} + */ + create : function() { + var out = new ArrayCtor(6); + matrix.identity(out); + + return out; + }, + /** + * 设置矩阵为单位矩阵 + * @param {Float32Array|Array.} out + */ + identity : function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }, + /** + * 复制矩阵 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} m + */ + copy: function(out, m) { + out[0] = m[0]; + out[1] = m[1]; + out[2] = m[2]; + out[3] = m[3]; + out[4] = m[4]; + out[5] = m[5]; + return out; + }, + /** + * 矩阵相乘 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} m1 + * @param {Float32Array|Array.} m2 + */ + mul : function (out, m1, m2) { + // Consider matrix.mul(m, m2, m); + // where out is the same as m2. + // So use temp variable to escape error. + var out0 = m1[0] * m2[0] + m1[2] * m2[1]; + var out1 = m1[1] * m2[0] + m1[3] * m2[1]; + var out2 = m1[0] * m2[2] + m1[2] * m2[3]; + var out3 = m1[1] * m2[2] + m1[3] * m2[3]; + var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4]; + var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5]; + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = out3; + out[4] = out4; + out[5] = out5; + return out; + }, + /** + * 平移变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {Float32Array|Array.} v + */ + translate : function(out, a, v) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4] + v[0]; + out[5] = a[5] + v[1]; + return out; + }, + /** + * 旋转变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {number} rad + */ + rotate : function(out, a, rad) { + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + var st = Math.sin(rad); + var ct = Math.cos(rad); + + out[0] = aa * ct + ab * st; + out[1] = -aa * st + ab * ct; + out[2] = ac * ct + ad * st; + out[3] = -ac * st + ct * ad; + out[4] = ct * atx + st * aty; + out[5] = ct * aty - st * atx; + return out; + }, + /** + * 缩放变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {Float32Array|Array.} v + */ + scale : function(out, a, v) { + var vx = v[0]; + var vy = v[1]; + out[0] = a[0] * vx; + out[1] = a[1] * vy; + out[2] = a[2] * vx; + out[3] = a[3] * vy; + out[4] = a[4] * vx; + out[5] = a[5] * vy; + return out; + }, + /** + * 求逆矩阵 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + */ + invert : function(out, a) { + + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + + var det = aa * ad - ab * ac; + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + } + }; + + module.exports = matrix; + + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/model/Model + */ + + + var zrUtil = __webpack_require__(4); + var clazzUtil = __webpack_require__(13); + + /** + * @alias module:echarts/model/Model + * @constructor + * @param {Object} option + * @param {module:echarts/model/Model} [parentModel] + * @param {module:echarts/model/Global} [ecModel] + */ + function Model(option, parentModel, ecModel) { + /** + * @type {module:echarts/model/Model} + * @readOnly + */ + this.parentModel = parentModel; + + /** + * @type {module:echarts/model/Global} + * @readOnly + */ + this.ecModel = ecModel; + + /** + * @type {Object} + * @protected + */ + this.option = option; + + // Simple optimization + // if (this.init) { + // if (arguments.length <= 4) { + // this.init(option, parentModel, ecModel, extraOpt); + // } + // else { + // this.init.apply(this, arguments); + // } + // } + } + + Model.prototype = { + + constructor: Model, + + /** + * Model 的初始化函数 + * @param {Object} option + */ + init: null, + + /** + * 从新的 Option merge + */ + mergeOption: function (option) { + zrUtil.merge(this.option, option, true); + }, + + /** + * @param {string} path + * @param {boolean} [ignoreParent=false] + * @return {*} + */ + get: function (path, ignoreParent) { + if (!path) { + return this.option; + } + + if (typeof path === 'string') { + path = path.split('.'); + } + + var obj = this.option; + var parentModel = this.parentModel; + for (var i = 0; i < path.length; i++) { + // Ignore empty + if (!path[i]) { + continue; + } + // obj could be number/string/... (like 0) + obj = (obj && typeof obj === 'object') ? obj[path[i]] : null; + if (obj == null) { + break; + } + } + if (obj == null && parentModel && !ignoreParent) { + obj = parentModel.get(path); + } + return obj; + }, + + /** + * @param {string} key + * @param {boolean} [ignoreParent=false] + * @return {*} + */ + getShallow: function (key, ignoreParent) { + var option = this.option; + + var val = option == null ? option : option[key]; + var parentModel = this.parentModel; + if (val == null && parentModel && !ignoreParent) { + val = parentModel.getShallow(key); + } + return val; + }, + + /** + * @param {string} path + * @param {module:echarts/model/Model} [parentModel] + * @return {module:echarts/model/Model} + */ + getModel: function (path, parentModel) { + var obj = this.get(path, true); + var thisParentModel = this.parentModel; + var model = new Model( + obj, parentModel || (thisParentModel && thisParentModel.getModel(path)), + this.ecModel + ); + return model; + }, + + /** + * If model has option + */ + isEmpty: function () { + return this.option == null; + }, + + restoreData: function () {}, + + // Pending + clone: function () { + var Ctor = this.constructor; + return new Ctor(zrUtil.clone(this.option)); + }, + + setReadOnly: function (properties) { + clazzUtil.setReadOnly(this, properties); + } + }; + + // Enable Model.extend. + clazzUtil.enableClassExtend(Model); + + var mixin = zrUtil.mixin; + mixin(Model, __webpack_require__(14)); + mixin(Model, __webpack_require__(16)); + mixin(Model, __webpack_require__(17)); + mixin(Model, __webpack_require__(18)); + + module.exports = Model; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var clazz = {}; + + var TYPE_DELIMITER = '.'; + var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; + /** + * @public + */ + var parseClassType = clazz.parseClassType = function (componentType) { + var ret = {main: '', sub: ''}; + if (componentType) { + componentType = componentType.split(TYPE_DELIMITER); + ret.main = componentType[0] || ''; + ret.sub = componentType[1] || ''; + } + return ret; + }; + /** + * @public + */ + clazz.enableClassExtend = function (RootClass) { + + RootClass.$constructor = RootClass; + RootClass.extend = function (proto) { + var superClass = this; + var ExtendedClass = function () { + if (!proto.$constructor) { + superClass.apply(this, arguments); + } + else { + proto.$constructor.apply(this, arguments); + } + }; + + zrUtil.extend(ExtendedClass.prototype, proto); + + ExtendedClass.extend = this.extend; + ExtendedClass.superCall = superCall; + ExtendedClass.superApply = superApply; + zrUtil.inherits(ExtendedClass, this); + ExtendedClass.superClass = superClass; + + return ExtendedClass; + }; + }; + + // superCall should have class info, which can not be fetch from 'this'. + // Consider this case: + // class A has method f, + // class B inherits class A, overrides method f, f call superApply('f'), + // class C inherits class B, do not overrides method f, + // then when method of class C is called, dead loop occured. + function superCall(context, methodName) { + var args = zrUtil.slice(arguments, 2); + return this.superClass.prototype[methodName].apply(context, args); + } + + function superApply(context, methodName, args) { + return this.superClass.prototype[methodName].apply(context, args); + } + + /** + * @param {Object} entity + * @param {Object} options + * @param {boolean} [options.registerWhenExtend] + * @public + */ + clazz.enableClassManagement = function (entity, options) { + options = options || {}; + + /** + * Component model classes + * key: componentType, + * value: + * componentClass, when componentType is 'xxx' + * or Object., when componentType is 'xxx.yy' + * @type {Object} + */ + var storage = {}; + + entity.registerClass = function (Clazz, componentType) { + if (componentType) { + componentType = parseClassType(componentType); + + if (!componentType.sub) { + if (true) { + if (storage[componentType.main]) { + console.warn(componentType.main + ' exists.'); + } + } + storage[componentType.main] = Clazz; + } + else if (componentType.sub !== IS_CONTAINER) { + var container = makeContainer(componentType); + container[componentType.sub] = Clazz; + } + } + return Clazz; + }; + + entity.getClass = function (componentTypeMain, subType, throwWhenNotFound) { + var Clazz = storage[componentTypeMain]; + + if (Clazz && Clazz[IS_CONTAINER]) { + Clazz = subType ? Clazz[subType] : null; + } + + if (throwWhenNotFound && !Clazz) { + throw new Error( + 'Component ' + componentTypeMain + '.' + (subType || '') + ' not exists. Load it first.' + ); + } + + return Clazz; + }; + + entity.getClassesByMainType = function (componentType) { + componentType = parseClassType(componentType); + + var result = []; + var obj = storage[componentType.main]; + + if (obj && obj[IS_CONTAINER]) { + zrUtil.each(obj, function (o, type) { + type !== IS_CONTAINER && result.push(o); + }); + } + else { + result.push(obj); + } + + return result; + }; + + entity.hasClass = function (componentType) { + // Just consider componentType.main. + componentType = parseClassType(componentType); + return !!storage[componentType.main]; + }; + + /** + * @return {Array.} Like ['aa', 'bb'], but can not be ['aa.xx'] + */ + entity.getAllClassMainTypes = function () { + var types = []; + zrUtil.each(storage, function (obj, type) { + types.push(type); + }); + return types; + }; + + /** + * If a main type is container and has sub types + * @param {string} mainType + * @return {boolean} + */ + entity.hasSubTypes = function (componentType) { + componentType = parseClassType(componentType); + var obj = storage[componentType.main]; + return obj && obj[IS_CONTAINER]; + }; + + entity.parseClassType = parseClassType; + + function makeContainer(componentType) { + var container = storage[componentType.main]; + if (!container || !container[IS_CONTAINER]) { + container = storage[componentType.main] = {}; + container[IS_CONTAINER] = true; + } + return container; + } + + if (options.registerWhenExtend) { + var originalExtend = entity.extend; + if (originalExtend) { + entity.extend = function (proto) { + var ExtendedClass = originalExtend.call(this, proto); + return entity.registerClass(ExtendedClass, proto.type); + }; + } + } + + return entity; + }; + + /** + * @param {string|Array.} properties + */ + clazz.setReadOnly = function (obj, properties) { + // FIXME It seems broken in IE8 simulation of IE11 + // if (!zrUtil.isArray(properties)) { + // properties = properties != null ? [properties] : []; + // } + // zrUtil.each(properties, function (prop) { + // var value = obj[prop]; + + // Object.defineProperty + // && Object.defineProperty(obj, prop, { + // value: value, writable: false + // }); + // zrUtil.isArray(obj[prop]) + // && Object.freeze + // && Object.freeze(obj[prop]); + // }); + }; + + module.exports = clazz; + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + + var getLineStyle = __webpack_require__(15)( + [ + ['lineWidth', 'width'], + ['stroke', 'color'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getLineStyle: function (excludes) { + var style = getLineStyle.call(this, excludes); + var lineDash = this.getLineDash(); + lineDash && (style.lineDash = lineDash); + return style; + }, + + getLineDash: function () { + var lineType = this.get('type'); + return (lineType === 'solid' || lineType == null) ? null + : (lineType === 'dashed' ? [5, 5] : [2, 2]); + } + }; + + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Parse shadow style + // TODO Only shallow path support + + var zrUtil = __webpack_require__(4); + + module.exports = function (properties) { + // Normalize + for (var i = 0; i < properties.length; i++) { + if (!properties[i][1]) { + properties[i][1] = properties[i][0]; + } + } + return function (excludes) { + var style = {}; + for (var i = 0; i < properties.length; i++) { + var propName = properties[i][1]; + if (excludes && zrUtil.indexOf(excludes, propName) >= 0) { + continue; + } + var val = this.getShallow(propName); + if (val != null) { + style[properties[i][0]] = val; + } + } + return style; + }; + }; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + + module.exports = { + getAreaStyle: __webpack_require__(15)( + [ + ['fill', 'color'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['opacity'], + ['shadowColor'] + ] + ) + }; + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + + + var textContain = __webpack_require__(8); + + function getShallow(model, path) { + return model && model.getShallow(path); + } + + module.exports = { + /** + * Get color property or get color from option.textStyle.color + * @return {string} + */ + getTextColor: function () { + var ecModel = this.ecModel; + return this.getShallow('color') + || (ecModel && ecModel.get('textStyle.color')); + }, + + /** + * Create font string from fontStyle, fontWeight, fontSize, fontFamily + * @return {string} + */ + getFont: function () { + var ecModel = this.ecModel; + var gTextStyleModel = ecModel && ecModel.getModel('textStyle'); + return [ + // FIXME in node-canvas fontWeight is before fontStyle + this.getShallow('fontStyle') || getShallow(gTextStyleModel, 'fontStyle'), + this.getShallow('fontWeight') || getShallow(gTextStyleModel, 'fontWeight'), + (this.getShallow('fontSize') || getShallow(gTextStyleModel, 'fontSize') || 12) + 'px', + this.getShallow('fontFamily') || getShallow(gTextStyleModel, 'fontFamily') || 'sans-serif' + ].join(' '); + }, + + getTextRect: function (text) { + var textStyle = this.get('textStyle') || {}; + return textContain.getBoundingRect( + text, + this.getFont(), + textStyle.align, + textStyle.baseline + ); + }, + + truncateText: function (text, containerWidth, ellipsis, options) { + return textContain.truncateText( + text, containerWidth, this.getFont(), ellipsis, options + ); + } + }; + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + + var getItemStyle = __webpack_require__(15)( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getItemStyle: function (excludes) { + var style = getItemStyle.call(this, excludes); + var lineDash = this.getBorderLineDash(); + lineDash && (style.lineDash = lineDash); + return style; + }, + + getBorderLineDash: function () { + var lineType = this.get('borderType'); + return (lineType === 'solid' || lineType == null) ? null + : (lineType === 'dashed' ? [5, 5] : [1, 1]); + } + }; + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Component model + * + * @module echarts/model/Component + */ + + + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + var arrayPush = Array.prototype.push; + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + var layout = __webpack_require__(21); + + /** + * @alias module:echarts/model/Component + * @constructor + * @param {Object} option + * @param {module:echarts/model/Model} parentModel + * @param {module:echarts/model/Model} ecModel + */ + var ComponentModel = Model.extend({ + + type: 'component', + + /** + * @readOnly + * @type {string} + */ + id: '', + + /** + * @readOnly + */ + name: '', + + /** + * @readOnly + * @type {string} + */ + mainType: '', + + /** + * @readOnly + * @type {string} + */ + subType: '', + + /** + * @readOnly + * @type {number} + */ + componentIndex: 0, + + /** + * @type {Object} + * @protected + */ + defaultOption: null, + + /** + * @type {module:echarts/model/Global} + * @readOnly + */ + ecModel: null, + + /** + * key: componentType + * value: Component model list, can not be null. + * @type {Object.>} + * @readOnly + */ + dependentModels: [], + + /** + * @type {string} + * @readOnly + */ + uid: null, + + /** + * Support merge layout params. + * Only support 'box' now (left/right/top/bottom/width/height). + * @type {string|Object} Object can be {ignoreSize: true} + * @readOnly + */ + layoutMode: null, + + $constructor: function (option, parentModel, ecModel, extraOpt) { + Model.call(this, option, parentModel, ecModel, extraOpt); + + // Set dependentModels, componentIndex, name, id, mainType, subType. + zrUtil.extend(this, extraOpt); + + this.uid = componentUtil.getUID('componentModel'); + }, + + + init: function (option, parentModel, ecModel, extraOpt) { + this.mergeDefaultAndTheme(option, ecModel); + }, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? layout.getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + zrUtil.merge(option, themeModel.get(this.mainType)); + zrUtil.merge(option, this.getDefaultOption()); + + if (layoutMode) { + layout.mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + mergeOption: function (option) { + zrUtil.merge(this.option, option, true); + + var layoutMode = this.layoutMode; + if (layoutMode) { + layout.mergeLayoutParam(this.option, option, layoutMode); + } + }, + + // Hooker after init or mergeOption + optionUpdated: function (newCptOption, isInit) {}, + + getDefaultOption: function () { + if (!this.hasOwnProperty('__defaultOption')) { + var optList = []; + var Class = this.constructor; + while (Class) { + var opt = Class.prototype.defaultOption; + opt && optList.push(opt); + Class = Class.superClass; + } + + var defaultOption = {}; + for (var i = optList.length - 1; i >= 0; i--) { + defaultOption = zrUtil.merge(defaultOption, optList[i], true); + } + this.__defaultOption = defaultOption; + } + return this.__defaultOption; + } + + }); + + // Reset ComponentModel.extend, add preConstruct. + // clazzUtil.enableClassExtend( + // ComponentModel, + // function (option, parentModel, ecModel, extraOpt) { + // // Set dependentModels, componentIndex, name, id, mainType, subType. + // zrUtil.extend(this, extraOpt); + + // this.uid = componentUtil.getUID('componentModel'); + + // // this.setReadOnly([ + // // 'type', 'id', 'uid', 'name', 'mainType', 'subType', + // // 'dependentModels', 'componentIndex' + // // ]); + // } + // ); + + // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement( + ComponentModel, {registerWhenExtend: true} + ); + componentUtil.enableSubTypeDefaulter(ComponentModel); + + // Add capability of ComponentModel.topologicalTravel. + componentUtil.enableTopologicalTravel(ComponentModel, getDependencies); + + function getDependencies(componentType) { + var deps = []; + zrUtil.each(ComponentModel.getClassesByMainType(componentType), function (Clazz) { + arrayPush.apply(deps, Clazz.prototype.dependencies || []); + }); + // Ensure main type + return zrUtil.map(deps, function (type) { + return clazzUtil.parseClassType(type).main; + }); + } + + zrUtil.mixin(ComponentModel, __webpack_require__(22)); + + module.exports = ComponentModel; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var clazz = __webpack_require__(13); + + var parseClassType = clazz.parseClassType; + + var base = 0; + + var componentUtil = {}; + + var DELIMITER = '_'; + + /** + * @public + * @param {string} type + * @return {string} + */ + componentUtil.getUID = function (type) { + // Considering the case of crossing js context, + // use Math.random to make id as unique as possible. + return [(type || ''), base++, Math.random()].join(DELIMITER); + }; + + /** + * @inner + */ + componentUtil.enableSubTypeDefaulter = function (entity) { + + var subTypeDefaulters = {}; + + entity.registerSubTypeDefaulter = function (componentType, defaulter) { + componentType = parseClassType(componentType); + subTypeDefaulters[componentType.main] = defaulter; + }; + + entity.determineSubType = function (componentType, option) { + var type = option.type; + if (!type) { + var componentTypeMain = parseClassType(componentType).main; + if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) { + type = subTypeDefaulters[componentTypeMain](option); + } + } + return type; + }; + + return entity; + }; + + /** + * Topological travel on Activity Network (Activity On Vertices). + * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. + * + * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. + * + * If there is circle dependencey, Error will be thrown. + * + */ + componentUtil.enableTopologicalTravel = function (entity, dependencyGetter) { + + /** + * @public + * @param {Array.} targetNameList Target Component type list. + * Can be ['aa', 'bb', 'aa.xx'] + * @param {Array.} fullNameList By which we can build dependency graph. + * @param {Function} callback Params: componentType, dependencies. + * @param {Object} context Scope of callback. + */ + entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) { + if (!targetNameList.length) { + return; + } + + var result = makeDepndencyGraph(fullNameList); + var graph = result.graph; + var stack = result.noEntryList; + + var targetNameSet = {}; + zrUtil.each(targetNameList, function (name) { + targetNameSet[name] = true; + }); + + while (stack.length) { + var currComponentType = stack.pop(); + var currVertex = graph[currComponentType]; + var isInTargetNameSet = !!targetNameSet[currComponentType]; + if (isInTargetNameSet) { + callback.call(context, currComponentType, currVertex.originalDeps.slice()); + delete targetNameSet[currComponentType]; + } + zrUtil.each( + currVertex.successor, + isInTargetNameSet ? removeEdgeAndAdd : removeEdge + ); + } + + zrUtil.each(targetNameSet, function () { + throw new Error('Circle dependency may exists'); + }); + + function removeEdge(succComponentType) { + graph[succComponentType].entryCount--; + if (graph[succComponentType].entryCount === 0) { + stack.push(succComponentType); + } + } + + // Consider this case: legend depends on series, and we call + // chart.setOption({series: [...]}), where only series is in option. + // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will + // not be called, but only sereis.mergeOption is called. Thus legend + // have no chance to update its local record about series (like which + // name of series is available in legend). + function removeEdgeAndAdd(succComponentType) { + targetNameSet[succComponentType] = true; + removeEdge(succComponentType); + } + }; + + /** + * DepndencyGraph: {Object} + * key: conponentType, + * value: { + * successor: [conponentTypes...], + * originalDeps: [conponentTypes...], + * entryCount: {number} + * } + */ + function makeDepndencyGraph(fullNameList) { + var graph = {}; + var noEntryList = []; + + zrUtil.each(fullNameList, function (name) { + + var thisItem = createDependencyGraphItem(graph, name); + var originalDeps = thisItem.originalDeps = dependencyGetter(name); + + var availableDeps = getAvailableDependencies(originalDeps, fullNameList); + thisItem.entryCount = availableDeps.length; + if (thisItem.entryCount === 0) { + noEntryList.push(name); + } + + zrUtil.each(availableDeps, function (dependentName) { + if (zrUtil.indexOf(thisItem.predecessor, dependentName) < 0) { + thisItem.predecessor.push(dependentName); + } + var thatItem = createDependencyGraphItem(graph, dependentName); + if (zrUtil.indexOf(thatItem.successor, dependentName) < 0) { + thatItem.successor.push(name); + } + }); + }); + + return {graph: graph, noEntryList: noEntryList}; + } + + function createDependencyGraphItem(graph, name) { + if (!graph[name]) { + graph[name] = {predecessor: [], successor: []}; + } + return graph[name]; + } + + function getAvailableDependencies(originalDeps, fullNameList) { + var availableDeps = []; + zrUtil.each(originalDeps, function (dep) { + zrUtil.indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); + }); + return availableDeps; + } + }; + + module.exports = componentUtil; + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Layout helpers for each component positioning + + + var zrUtil = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var parsePercent = numberUtil.parsePercent; + var each = zrUtil.each; + + var layout = {}; + + var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height']; + + function boxLayout(orient, group, gap, maxWidth, maxHeight) { + var x = 0; + var y = 0; + if (maxWidth == null) { + maxWidth = Infinity; + } + if (maxHeight == null) { + maxHeight = Infinity; + } + var currentLineMaxSize = 0; + group.eachChild(function (child, idx) { + var position = child.position; + var rect = child.getBoundingRect(); + var nextChild = group.childAt(idx + 1); + var nextChildRect = nextChild && nextChild.getBoundingRect(); + var nextX; + var nextY; + if (orient === 'horizontal') { + var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0); + nextX = x + moveX; + // Wrap when width exceeds maxWidth or meet a `newline` group + if (nextX > maxWidth || child.newline) { + x = 0; + nextX = moveX; + y += currentLineMaxSize + gap; + currentLineMaxSize = rect.height; + } + else { + currentLineMaxSize = Math.max(currentLineMaxSize, rect.height); + } + } + else { + var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0); + nextY = y + moveY; + // Wrap when width exceeds maxHeight or meet a `newline` group + if (nextY > maxHeight || child.newline) { + x += currentLineMaxSize + gap; + y = 0; + nextY = moveY; + currentLineMaxSize = rect.width; + } + else { + currentLineMaxSize = Math.max(currentLineMaxSize, rect.width); + } + } + + if (child.newline) { + return; + } + + position[0] = x; + position[1] = y; + + orient === 'horizontal' + ? (x = nextX + gap) + : (y = nextY + gap); + }); + } + + /** + * VBox or HBox layouting + * @param {string} orient + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.box = boxLayout; + + /** + * VBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.vbox = zrUtil.curry(boxLayout, 'vertical'); + + /** + * HBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.hbox = zrUtil.curry(boxLayout, 'horizontal'); + + /** + * If x or x2 is not specified or 'center' 'left' 'right', + * the width would be as long as possible. + * If y or y2 is not specified or 'middle' 'top' 'bottom', + * the height would be as long as possible. + * + * @param {Object} positionInfo + * @param {number|string} [positionInfo.x] + * @param {number|string} [positionInfo.y] + * @param {number|string} [positionInfo.x2] + * @param {number|string} [positionInfo.y2] + * @param {Object} containerRect + * @param {string|number} margin + * @return {Object} {width, height} + */ + layout.getAvailableSize = function (positionInfo, containerRect, margin) { + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var x = parsePercent(positionInfo.x, containerWidth); + var y = parsePercent(positionInfo.y, containerHeight); + var x2 = parsePercent(positionInfo.x2, containerWidth); + var y2 = parsePercent(positionInfo.y2, containerHeight); + + (isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0); + (isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth); + (isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0); + (isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight); + + margin = formatUtil.normalizeCssArray(margin || 0); + + return { + width: Math.max(x2 - x - margin[1] - margin[3], 0), + height: Math.max(y2 - y - margin[0] - margin[2], 0) + }; + }; + + /** + * Parse position info. + * + * @param {Object} positionInfo + * @param {number|string} [positionInfo.left] + * @param {number|string} [positionInfo.top] + * @param {number|string} [positionInfo.right] + * @param {number|string} [positionInfo.bottom] + * @param {number|string} [positionInfo.width] + * @param {number|string} [positionInfo.height] + * @param {number|string} [positionInfo.aspect] Aspect is width / height + * @param {Object} containerRect + * @param {string|number} [margin] + * + * @return {module:zrender/core/BoundingRect} + */ + layout.getLayoutRect = function ( + positionInfo, containerRect, margin + ) { + margin = formatUtil.normalizeCssArray(margin || 0); + + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var left = parsePercent(positionInfo.left, containerWidth); + var top = parsePercent(positionInfo.top, containerHeight); + var right = parsePercent(positionInfo.right, containerWidth); + var bottom = parsePercent(positionInfo.bottom, containerHeight); + var width = parsePercent(positionInfo.width, containerWidth); + var height = parsePercent(positionInfo.height, containerHeight); + + var verticalMargin = margin[2] + margin[0]; + var horizontalMargin = margin[1] + margin[3]; + var aspect = positionInfo.aspect; + + // If width is not specified, calculate width from left and right + if (isNaN(width)) { + width = containerWidth - right - horizontalMargin - left; + } + if (isNaN(height)) { + height = containerHeight - bottom - verticalMargin - top; + } + + // If width and height are not given + // 1. Graph should not exceeds the container + // 2. Aspect must be keeped + // 3. Graph should take the space as more as possible + if (isNaN(width) && isNaN(height)) { + if (aspect > containerWidth / containerHeight) { + width = containerWidth * 0.8; + } + else { + height = containerHeight * 0.8; + } + } + + if (aspect != null) { + // Calculate width or height with given aspect + if (isNaN(width)) { + width = aspect * height; + } + if (isNaN(height)) { + height = width / aspect; + } + } + + // If left is not specified, calculate left from right and width + if (isNaN(left)) { + left = containerWidth - right - width - horizontalMargin; + } + if (isNaN(top)) { + top = containerHeight - bottom - height - verticalMargin; + } + + // Align left and top + switch (positionInfo.left || positionInfo.right) { + case 'center': + left = containerWidth / 2 - width / 2 - margin[3]; + break; + case 'right': + left = containerWidth - width - horizontalMargin; + break; + } + switch (positionInfo.top || positionInfo.bottom) { + case 'middle': + case 'center': + top = containerHeight / 2 - height / 2 - margin[0]; + break; + case 'bottom': + top = containerHeight - height - verticalMargin; + break; + } + // If something is wrong and left, top, width, height are calculated as NaN + left = left || 0; + top = top || 0; + if (isNaN(width)) { + // Width may be NaN if only one value is given except width + width = containerWidth - left - (right || 0); + } + if (isNaN(height)) { + // Height may be NaN if only one value is given except height + height = containerHeight - top - (bottom || 0); + } + + var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); + rect.margin = margin; + return rect; + }; + + /** + * Position group of component in viewport + * Group position is specified by either + * {left, top}, {right, bottom} + * If all properties exists, right and bottom will be igonred. + * + * @param {module:zrender/container/Group} group + * @param {Object} positionInfo + * @param {number|string} [positionInfo.left] + * @param {number|string} [positionInfo.top] + * @param {number|string} [positionInfo.right] + * @param {number|string} [positionInfo.bottom] + * @param {Object} containerRect + * @param {string|number} margin + */ + layout.positionGroup = function ( + group, positionInfo, containerRect, margin + ) { + var groupRect = group.getBoundingRect(); + + positionInfo = zrUtil.extend(zrUtil.clone(positionInfo), { + width: groupRect.width, + height: groupRect.height + }); + + positionInfo = layout.getLayoutRect( + positionInfo, containerRect, margin + ); + + group.attr('position', [ + positionInfo.x - groupRect.x, + positionInfo.y - groupRect.y + ]); + }; + + /** + * Consider Case: + * When defulat option has {left: 0, width: 100}, and we set {right: 0} + * through setOption or media query, using normal zrUtil.merge will cause + * {right: 0} does not take effect. + * + * @example + * ComponentModel.extend({ + * init: function () { + * ... + * var inputPositionParams = layout.getLayoutParams(option); + * this.mergeOption(inputPositionParams); + * }, + * mergeOption: function (newOption) { + * newOption && zrUtil.merge(thisOption, newOption, true); + * layout.mergeLayoutParam(thisOption, newOption); + * } + * }); + * + * @param {Object} targetOption + * @param {Object} newOption + * @param {Object|string} [opt] + * @param {boolean} [opt.ignoreSize=false] Some component must has width and height. + */ + layout.mergeLayoutParam = function (targetOption, newOption, opt) { + !zrUtil.isObject(opt) && (opt = {}); + var hNames = ['width', 'left', 'right']; // Order by priority. + var vNames = ['height', 'top', 'bottom']; // Order by priority. + var hResult = merge(hNames); + var vResult = merge(vNames); + + copy(hNames, targetOption, hResult); + copy(vNames, targetOption, vResult); + + function merge(names) { + var newParams = {}; + var newValueCount = 0; + var merged = {}; + var mergedValueCount = 0; + var enoughParamNumber = opt.ignoreSize ? 1 : 2; + + each(names, function (name) { + merged[name] = targetOption[name]; + }); + each(names, function (name) { + // Consider case: newOption.width is null, which is + // set by user for removing width setting. + hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]); + hasValue(newParams, name) && newValueCount++; + hasValue(merged, name) && mergedValueCount++; + }); + + // Case: newOption: {width: ..., right: ...}, + // or targetOption: {right: ...} and newOption: {width: ...}, + // There is no conflict when merged only has params count + // little than enoughParamNumber. + if (mergedValueCount === enoughParamNumber || !newValueCount) { + return merged; + } + // Case: newOption: {width: ..., right: ...}, + // Than we can make sure user only want those two, and ignore + // all origin params in targetOption. + else if (newValueCount >= enoughParamNumber) { + return newParams; + } + else { + // Chose another param from targetOption by priority. + // When 'ignoreSize', enoughParamNumber is 1 and those will not happen. + for (var i = 0; i < names.length; i++) { + var name = names[i]; + if (!hasProp(newParams, name) && hasProp(targetOption, name)) { + newParams[name] = targetOption[name]; + break; + } + } + return newParams; + } + } + + function hasProp(obj, name) { + return obj.hasOwnProperty(name); + } + + function hasValue(obj, name) { + return obj[name] != null && obj[name] !== 'auto'; + } + + function copy(names, target, source) { + each(names, function (name) { + target[name] = source[name]; + }); + } + }; + + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ + layout.getLayoutParams = function (source) { + return layout.copyLayoutParams({}, source); + }; + + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ + layout.copyLayoutParams = function (target, source) { + source && target && each(LOCATION_PARAMS, function (name) { + source.hasOwnProperty(name) && (target[name] = source[name]); + }); + return target; + }; + + module.exports = layout; + + +/***/ }, +/* 22 */ +/***/ function(module, exports) { + + + + module.exports = { + getBoxLayoutParams: function () { + return { + left: this.get('left'), + top: this.get('top'), + right: this.get('right'), + bottom: this.get('bottom'), + width: this.get('width'), + height: this.get('height') + }; + } + }; + + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + + var platform = ''; + // Navigator not exists in node + if (typeof navigator !== 'undefined') { + platform = navigator.platform || ''; + } + module.exports = { + // 全图默认背景 + // backgroundColor: 'rgba(0,0,0,0)', + + // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization + // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'], + // 浅色 + // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'], + // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'], + // 深色 + color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'], + + // 默认需要 Grid 配置项 + // grid: {}, + // 主题,主题 + textStyle: { + // color: '#000', + // decoration: 'none', + // PENDING + fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif', + // fontFamily: 'Arial, Verdana, sans-serif', + fontSize: 12, + fontStyle: 'normal', + fontWeight: 'normal' + }, + + // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/ + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + // Default is source-over + blendMode: null, + + animation: true, + animationDuration: 1000, + animationDurationUpdate: 300, + animationEasing: 'exponentialOut', + animationEasingUpdate: 'cubicOut', + + animationThreshold: 2000, + // Configuration for progressive/incremental rendering + progressiveThreshold: 3000, + progressive: 400, + + // Threshold of if use single hover layer to optimize. + hoverLayerThreshold: 3000 + }; + + +/***/ }, +/* 24 */ +/***/ function(module, exports) { + + + module.exports = { + clearColorPalette: function () { + this._colorIdx = 0; + this._colorNameMap = {}; + }, + + getColorFromPalette: function (name, scope) { + scope = scope || this; + var colorIdx = scope._colorIdx || 0; + var colorNameMap = scope._colorNameMap || (scope._colorNameMap = {}); + if (colorNameMap[name]) { + return colorNameMap[name]; + } + var colorPalette = this.get('color', true) || []; + if (!colorPalette.length) { + return; + } + + var color = colorPalette[colorIdx]; + if (name) { + colorNameMap[name] = color; + } + scope._colorIdx = (colorIdx + 1) % colorPalette.length; + + return color; + } + }; + + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var echartsAPIList = [ + 'getDom', 'getZr', 'getWidth', 'getHeight', 'dispatchAction', 'isDisposed', + 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption' + ]; + + function ExtensionAPI(chartInstance) { + zrUtil.each(echartsAPIList, function (name) { + this[name] = zrUtil.bind(chartInstance[name], chartInstance); + }, this); + } + + module.exports = ExtensionAPI; + + +/***/ }, +/* 26 */ +/***/ function(module, exports) { + + 'use strict'; + + + var coordinateSystemCreators = {}; + + function CoordinateSystemManager() { + + this._coordinateSystems = []; + } + + CoordinateSystemManager.prototype = { + + constructor: CoordinateSystemManager, + + create: function (ecModel, api) { + var coordinateSystems = []; + for (var type in coordinateSystemCreators) { + var list = coordinateSystemCreators[type].create(ecModel, api); + list && (coordinateSystems = coordinateSystems.concat(list)); + } + + this._coordinateSystems = coordinateSystems; + }, + + update: function (ecModel, api) { + var coordinateSystems = this._coordinateSystems; + for (var i = 0; i < coordinateSystems.length; i++) { + // FIXME MUST have + coordinateSystems[i].update && coordinateSystems[i].update(ecModel, api); + } + } + }; + + CoordinateSystemManager.register = function (type, coordinateSystemCreator) { + coordinateSystemCreators[type] = coordinateSystemCreator; + }; + + CoordinateSystemManager.get = function (type) { + return coordinateSystemCreators[type]; + }; + + module.exports = CoordinateSystemManager; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * ECharts option manager + * + * @module {echarts/model/OptionManager} + */ + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var each = zrUtil.each; + var clone = zrUtil.clone; + var map = zrUtil.map; + var merge = zrUtil.merge; + + var QUERY_REG = /^(min|max)?(.+)$/; + + /** + * TERM EXPLANATIONS: + * + * [option]: + * + * An object that contains definitions of components. For example: + * var option = { + * title: {...}, + * legend: {...}, + * visualMap: {...}, + * series: [ + * {data: [...]}, + * {data: [...]}, + * ... + * ] + * }; + * + * [rawOption]: + * + * An object input to echarts.setOption. 'rawOption' may be an + * 'option', or may be an object contains multi-options. For example: + * var option = { + * baseOption: { + * title: {...}, + * legend: {...}, + * series: [ + * {data: [...]}, + * {data: [...]}, + * ... + * ] + * }, + * timeline: {...}, + * options: [ + * {title: {...}, series: {data: [...]}}, + * {title: {...}, series: {data: [...]}}, + * ... + * ], + * media: [ + * { + * query: {maxWidth: 320}, + * option: {series: {x: 20}, visualMap: {show: false}} + * }, + * { + * query: {minWidth: 320, maxWidth: 720}, + * option: {series: {x: 500}, visualMap: {show: true}} + * }, + * { + * option: {series: {x: 1200}, visualMap: {show: true}} + * } + * ] + * }; + * + * @alias module:echarts/model/OptionManager + * @param {module:echarts/ExtensionAPI} api + */ + function OptionManager(api) { + + /** + * @private + * @type {module:echarts/ExtensionAPI} + */ + this._api = api; + + /** + * @private + * @type {Array.} + */ + this._timelineOptions = []; + + /** + * @private + * @type {Array.} + */ + this._mediaList = []; + + /** + * @private + * @type {Object} + */ + this._mediaDefault; + + /** + * -1, means default. + * empty means no media. + * @private + * @type {Array.} + */ + this._currentMediaIndices = []; + + /** + * @private + * @type {Object} + */ + this._optionBackup; + + /** + * @private + * @type {Object} + */ + this._newBaseOption; + } + + // timeline.notMerge is not supported in ec3. Firstly there is rearly + // case that notMerge is needed. Secondly supporting 'notMerge' requires + // rawOption cloned and backuped when timeline changed, which does no + // good to performance. What's more, that both timeline and setOption + // method supply 'notMerge' brings complex and some problems. + // Consider this case: + // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); + // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); + + OptionManager.prototype = { + + constructor: OptionManager, + + /** + * @public + * @param {Object} rawOption Raw option. + * @param {module:echarts/model/Global} ecModel + * @param {Array.} optionPreprocessorFuncs + * @return {Object} Init option + */ + setOption: function (rawOption, optionPreprocessorFuncs) { + rawOption = clone(rawOption, true); + + // FIXME + // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。 + + var oldOptionBackup = this._optionBackup; + var newParsedOption = parseRawOption.call( + this, rawOption, optionPreprocessorFuncs, !oldOptionBackup + ); + this._newBaseOption = newParsedOption.baseOption; + + // For setOption at second time (using merge mode); + if (oldOptionBackup) { + // Only baseOption can be merged. + mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption); + + // For simplicity, timeline options and media options do not support merge, + // that is, if you `setOption` twice and both has timeline options, the latter + // timeline opitons will not be merged to the formers, but just substitude them. + if (newParsedOption.timelineOptions.length) { + oldOptionBackup.timelineOptions = newParsedOption.timelineOptions; + } + if (newParsedOption.mediaList.length) { + oldOptionBackup.mediaList = newParsedOption.mediaList; + } + if (newParsedOption.mediaDefault) { + oldOptionBackup.mediaDefault = newParsedOption.mediaDefault; + } + } + else { + this._optionBackup = newParsedOption; + } + }, + + /** + * @param {boolean} isRecreate + * @return {Object} + */ + mountOption: function (isRecreate) { + var optionBackup = this._optionBackup; + + // TODO + // 如果没有reset功能则不clone。 + + this._timelineOptions = map(optionBackup.timelineOptions, clone); + this._mediaList = map(optionBackup.mediaList, clone); + this._mediaDefault = clone(optionBackup.mediaDefault); + this._currentMediaIndices = []; + + return clone(isRecreate + // this._optionBackup.baseOption, which is created at the first `setOption` + // called, and is merged into every new option by inner method `mergeOption` + // each time `setOption` called, can be only used in `isRecreate`, because + // its reliability is under suspicion. In other cases option merge is + // proformed by `model.mergeOption`. + ? optionBackup.baseOption : this._newBaseOption + ); + }, + + /** + * @param {module:echarts/model/Global} ecModel + * @return {Object} + */ + getTimelineOption: function (ecModel) { + var option; + var timelineOptions = this._timelineOptions; + + if (timelineOptions.length) { + // getTimelineOption can only be called after ecModel inited, + // so we can get currentIndex from timelineModel. + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel) { + option = clone( + timelineOptions[timelineModel.getCurrentIndex()], + true + ); + } + } + + return option; + }, + + /** + * @param {module:echarts/model/Global} ecModel + * @return {Array.} + */ + getMediaOption: function (ecModel) { + var ecWidth = this._api.getWidth(); + var ecHeight = this._api.getHeight(); + var mediaList = this._mediaList; + var mediaDefault = this._mediaDefault; + var indices = []; + var result = []; + + // No media defined. + if (!mediaList.length && !mediaDefault) { + return result; + } + + // Multi media may be applied, the latter defined media has higher priority. + for (var i = 0, len = mediaList.length; i < len; i++) { + if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { + indices.push(i); + } + } + + // FIXME + // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。 + if (!indices.length && mediaDefault) { + indices = [-1]; + } + + if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { + result = map(indices, function (index) { + return clone( + index === -1 ? mediaDefault.option : mediaList[index].option + ); + }); + } + // Otherwise return nothing. + + this._currentMediaIndices = indices; + + return result; + } + }; + + function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) { + var timelineOptions = []; + var mediaList = []; + var mediaDefault; + var baseOption; + + // Compatible with ec2. + var timelineOpt = rawOption.timeline; + + if (rawOption.baseOption) { + baseOption = rawOption.baseOption; + } + + // For timeline + if (timelineOpt || rawOption.options) { + baseOption = baseOption || {}; + timelineOptions = (rawOption.options || []).slice(); + } + + // For media query + if (rawOption.media) { + baseOption = baseOption || {}; + var media = rawOption.media; + each(media, function (singleMedia) { + if (singleMedia && singleMedia.option) { + if (singleMedia.query) { + mediaList.push(singleMedia); + } + else if (!mediaDefault) { + // Use the first media default. + mediaDefault = singleMedia; + } + } + }); + } + + // For normal option + if (!baseOption) { + baseOption = rawOption; + } + + // Set timelineOpt to baseOption in ec3, + // which is convenient for merge option. + if (!baseOption.timeline) { + baseOption.timeline = timelineOpt; + } + + // Preprocess. + each([baseOption].concat(timelineOptions) + .concat(zrUtil.map(mediaList, function (media) { + return media.option; + })), + function (option) { + each(optionPreprocessorFuncs, function (preProcess) { + preProcess(option, isNew); + }); + } + ); + + return { + baseOption: baseOption, + timelineOptions: timelineOptions, + mediaDefault: mediaDefault, + mediaList: mediaList + }; + } + + /** + * @see + * Support: width, height, aspectRatio + * Can use max or min as prefix. + */ + function applyMediaQuery(query, ecWidth, ecHeight) { + var realMap = { + width: ecWidth, + height: ecHeight, + aspectratio: ecWidth / ecHeight // lowser case for convenientce. + }; + + var applicatable = true; + + zrUtil.each(query, function (value, attr) { + var matched = attr.match(QUERY_REG); + + if (!matched || !matched[1] || !matched[2]) { + return; + } + + var operator = matched[1]; + var realAttr = matched[2].toLowerCase(); + + if (!compare(realMap[realAttr], value, operator)) { + applicatable = false; + } + }); + + return applicatable; + } + + function compare(real, expect, operator) { + if (operator === 'min') { + return real >= expect; + } + else if (operator === 'max') { + return real <= expect; + } + else { // Equals + return real === expect; + } + } + + function indicesEquals(indices1, indices2) { + // indices is always order by asc and has only finite number. + return indices1.join(',') === indices2.join(','); + } + + /** + * Consider case: + * `chart.setOption(opt1);` + * Then user do some interaction like dataZoom, dataView changing. + * `chart.setOption(opt2);` + * Then user press 'reset button' in toolbox. + * + * After doing that all of the interaction effects should be reset, the + * chart should be the same as the result of invoke + * `chart.setOption(opt1); chart.setOption(opt2);`. + * + * Although it is not able ensure that + * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to + * `chart.setOption(merge(opt1, opt2));` exactly, + * this might be the only simple way to implement that feature. + * + * MEMO: We've considered some other approaches: + * 1. Each model handle its self restoration but not uniform treatment. + * (Too complex in logic and error-prone) + * 2. Use a shadow ecModel. (Performace expensive) + */ + function mergeOption(oldOption, newOption) { + newOption = newOption || {}; + + each(newOption, function (newCptOpt, mainType) { + if (newCptOpt == null) { + return; + } + + var oldCptOpt = oldOption[mainType]; + + if (!ComponentModel.hasClass(mainType)) { + oldOption[mainType] = merge(oldCptOpt, newCptOpt, true); + } + else { + newCptOpt = modelUtil.normalizeToArray(newCptOpt); + oldCptOpt = modelUtil.normalizeToArray(oldCptOpt); + + var mapResult = modelUtil.mappingToExists(oldCptOpt, newCptOpt); + + oldOption[mainType] = map(mapResult, function (item) { + return (item.option && item.exist) + ? merge(item.exist, item.option, true) + : (item.exist || item.option); + }); + } + }); + } + + module.exports = OptionManager; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var colorPaletteMixin = __webpack_require__(24); + var env = __webpack_require__(2); + + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var SeriesModel = ComponentModel.extend({ + + type: 'series.__base__', + + /** + * @readOnly + */ + seriesIndex: 0, + + // coodinateSystem will be injected in the echarts/CoordinateSystem + coordinateSystem: null, + + /** + * @type {Object} + * @protected + */ + defaultOption: null, + + /** + * Data provided for legend + * @type {Function} + */ + // PENDING + legendDataProvider: null, + + /** + * Access path of color for visual + */ + visualColorAccessPath: 'itemStyle.normal.color', + + init: function (option, parentModel, ecModel, extraOpt) { + + /** + * @type {number} + * @readOnly + */ + this.seriesIndex = this.componentIndex; + + this.mergeDefaultAndTheme(option, ecModel); + + /** + * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph} + * @private + */ + this._dataBeforeProcessed = this.getInitialData(option, ecModel); + + // If we reverse the order (make this._data firstly, and then make + // this._dataBeforeProcessed by cloneShallow), cloneShallow will + // cause this._data.graph.data !== this._data when using + // module:echarts/data/Graph or module:echarts/data/Tree. + // See module:echarts/data/helper/linkList + this._data = this._dataBeforeProcessed.cloneShallow(); + }, + + /** + * Util for merge default and theme to option + * @param {Object} option + * @param {module:echarts/model/Global} ecModel + */ + mergeDefaultAndTheme: function (option, ecModel) { + zrUtil.merge( + option, + ecModel.getTheme().get(this.subType) + ); + zrUtil.merge(option, this.getDefaultOption()); + + // Default label emphasis `position` and `show` + // FIXME Set label in mergeOption + modelUtil.defaultEmphasis(option.label, modelUtil.LABEL_OPTIONS); + + this.fillDataTextStyle(option.data); + }, + + mergeOption: function (newSeriesOption, ecModel) { + newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true); + this.fillDataTextStyle(newSeriesOption.data); + + var data = this.getInitialData(newSeriesOption, ecModel); + // TODO Merge data? + if (data) { + this._data = data; + this._dataBeforeProcessed = data.cloneShallow(); + } + }, + + fillDataTextStyle: function (data) { + // Default data label emphasis `position` and `show` + // FIXME Tree structure data ? + // FIXME Performance ? + if (data) { + for (var i = 0; i < data.length; i++) { + if (data[i] && data[i].label) { + modelUtil.defaultEmphasis(data[i].label, modelUtil.LABEL_OPTIONS); + } + } + } + }, + + /** + * Init a data structure from data related option in series + * Must be overwritten + */ + getInitialData: function () {}, + + /** + * @param {string} [dataType] + * @return {module:echarts/data/List} + */ + getData: function (dataType) { + return dataType == null ? this._data : this._data.getLinkedData(dataType); + }, + + /** + * @param {module:echarts/data/List} data + */ + setData: function (data) { + this._data = data; + }, + + /** + * Get data before processed + * @return {module:echarts/data/List} + */ + getRawData: function () { + return this._dataBeforeProcessed; + }, + + /** + * Coord dimension to data dimension. + * + * By default the result is the same as dimensions of series data. + * But in some series data dimensions are different from coord dimensions (i.e. + * candlestick and boxplot). Override this method to handle those cases. + * + * Coord dimension to data dimension can be one-to-many + * + * @param {string} coordDim + * @return {Array.} dimensions on the axis. + */ + coordDimToDataDim: function (coordDim) { + return [coordDim]; + }, + + /** + * Convert data dimension to coord dimension. + * + * @param {string|number} dataDim + * @return {string} + */ + dataDimToCoordDim: function (dataDim) { + return dataDim; + }, + + /** + * Get base axis if has coordinate system and has axis. + * By default use coordSys.getBaseAxis(); + * Can be overrided for some chart. + * @return {type} description + */ + getBaseAxis: function () { + var coordSys = this.coordinateSystem; + return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); + }, + + // FIXME + /** + * Default tooltip formatter + * + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {number} [dataType] + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + function formatArrayValue(value) { + return zrUtil.map(value, function (val, idx) { + var dimInfo = data.getDimensionInfo(idx); + var dimType = dimInfo && dimInfo.type; + if (dimType === 'ordinal') { + return val; + } + else if (dimType === 'time') { + return multipleSeries ? '' : formatUtil.formatTime('yyyy/mm/dd hh:mm:ss', val); + } + else { + return addCommas(val); + } + }).filter(function (val) { + return !!val; + }).join(', '); + } + + var data = this._data; + + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? formatArrayValue(value) : addCommas(value); + var name = data.getName(dataIndex); + var color = data.getItemVisual(dataIndex, 'color'); + var colorEl = ''; + + var seriesName = this.name; + // FIXME + if (seriesName === '\0-') { + // Not show '-' + seriesName = ''; + } + return !multipleSeries + ? ((seriesName && encodeHTML(seriesName) + '
') + colorEl + + (name + ? encodeHTML(name) + ' : ' + formattedValue + : formattedValue) + ) + : (colorEl + encodeHTML(this.name) + ' : ' + formattedValue); + }, + + /** + * @return {boolean} + */ + ifEnableAnimation: function () { + if (env.node) { + return false; + } + + var animationEnabled = this.getShallow('animation'); + if (animationEnabled) { + if (this.getData().count() > this.getShallow('animationThreshold')) { + animationEnabled = false; + } + } + return animationEnabled; + }, + + restoreData: function () { + this._data = this._dataBeforeProcessed.cloneShallow(); + }, + + getColorFromPalette: function (name, scope) { + var ecModel = this.ecModel; + // PENDING + var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope); + if (!color) { + color = ecModel.getColorFromPalette(name, scope); + } + return color; + }, + + getAxisTooltipDataIndex: null + }); + + zrUtil.mixin(SeriesModel, modelUtil.dataFormatMixin); + zrUtil.mixin(SeriesModel, colorPaletteMixin); + + module.exports = SeriesModel; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Group = __webpack_require__(30); + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + + var Component = function () { + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = componentUtil.getUID('viewComponent'); + }; + + Component.prototype = { + + constructor: Component, + + init: function (ecModel, api) {}, + + render: function (componentModel, ecModel, api, payload) {}, + + dispose: function () {} + }; + + var componentProto = Component.prototype; + componentProto.updateView + = componentProto.updateLayout + = componentProto.updateVisual + = function (seriesModel, ecModel, api, payload) { + // Do nothing; + }; + // Enable Component.extend. + clazzUtil.enableClassExtend(Component); + + // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement(Component, {registerWhenExtend: true}); + + module.exports = Component; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上 + * @module zrender/graphic/Group + * @example + * var Group = require('zrender/lib/container/Group'); + * var Circle = require('zrender/lib/graphic/shape/Circle'); + * var g = new Group(); + * g.position[0] = 100; + * g.position[1] = 100; + * g.add(new Circle({ + * style: { + * x: 100, + * y: 100, + * r: 20, + * } + * })); + * zr.add(g); + */ + + + var zrUtil = __webpack_require__(4); + var Element = __webpack_require__(31); + var BoundingRect = __webpack_require__(9); + + /** + * @alias module:zrender/graphic/Group + * @constructor + * @extends module:zrender/mixin/Transformable + * @extends module:zrender/mixin/Eventful + */ + var Group = function (opts) { + + opts = opts || {}; + + Element.call(this, opts); + + for (var key in opts) { + this[key] = opts[key]; + } + + this._children = []; + + this.__storage = null; + + this.__dirty = true; + }; + + Group.prototype = { + + constructor: Group, + + isGroup: true, + + /** + * @type {string} + */ + type: 'group', + + /** + * 所有子孙元素是否响应鼠标事件 + * @name module:/zrender/container/Group#silent + * @type {boolean} + * @default false + */ + silent: false, + + /** + * @return {Array.} + */ + children: function () { + return this._children.slice(); + }, + + /** + * 获取指定 index 的儿子节点 + * @param {number} idx + * @return {module:zrender/Element} + */ + childAt: function (idx) { + return this._children[idx]; + }, + + /** + * 获取指定名字的儿子节点 + * @param {string} name + * @return {module:zrender/Element} + */ + childOfName: function (name) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + if (children[i].name === name) { + return children[i]; + } + } + }, + + /** + * @return {number} + */ + childCount: function () { + return this._children.length; + }, + + /** + * 添加子节点到最后 + * @param {module:zrender/Element} child + */ + add: function (child) { + if (child && child !== this && child.parent !== this) { + + this._children.push(child); + + this._doAdd(child); + } + + return this; + }, + + /** + * 添加子节点在 nextSibling 之前 + * @param {module:zrender/Element} child + * @param {module:zrender/Element} nextSibling + */ + addBefore: function (child, nextSibling) { + if (child && child !== this && child.parent !== this + && nextSibling && nextSibling.parent === this) { + + var children = this._children; + var idx = children.indexOf(nextSibling); + + if (idx >= 0) { + children.splice(idx, 0, child); + this._doAdd(child); + } + } + + return this; + }, + + _doAdd: function (child) { + if (child.parent) { + child.parent.remove(child); + } + + child.parent = this; + + var storage = this.__storage; + var zr = this.__zr; + if (storage && storage !== child.__storage) { + + storage.addToMap(child); + + if (child instanceof Group) { + child.addChildrenToStorage(storage); + } + } + + zr && zr.refresh(); + }, + + /** + * 移除子节点 + * @param {module:zrender/Element} child + */ + remove: function (child) { + var zr = this.__zr; + var storage = this.__storage; + var children = this._children; + + var idx = zrUtil.indexOf(children, child); + if (idx < 0) { + return this; + } + children.splice(idx, 1); + + child.parent = null; + + if (storage) { + + storage.delFromMap(child.id); + + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + + zr && zr.refresh(); + + return this; + }, + + /** + * 移除所有子节点 + */ + removeAll: function () { + var children = this._children; + var storage = this.__storage; + var child; + var i; + for (i = 0; i < children.length; i++) { + child = children[i]; + if (storage) { + storage.delFromMap(child.id); + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + child.parent = null; + } + children.length = 0; + + return this; + }, + + /** + * 遍历所有子节点 + * @param {Function} cb + * @param {} context + */ + eachChild: function (cb, context) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + cb.call(context, child, i); + } + return this; + }, + + /** + * 深度优先遍历所有子孙节点 + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + cb.call(context, child); + + if (child.type === 'group') { + child.traverse(cb, context); + } + } + return this; + }, + + addChildrenToStorage: function (storage) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + storage.addToMap(child); + if (child instanceof Group) { + child.addChildrenToStorage(storage); + } + } + }, + + delChildrenFromStorage: function (storage) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + storage.delFromMap(child.id); + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + }, + + dirty: function () { + this.__dirty = true; + this.__zr && this.__zr.refresh(); + return this; + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function (includeChildren) { + // TODO Caching + // TODO Transform + var rect = null; + var tmpRect = new BoundingRect(0, 0, 0, 0); + var children = includeChildren || this._children; + var tmpMat = []; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.ignore || child.invisible) { + continue; + } + + var childRect = child.getBoundingRect(); + var transform = child.getLocalTransform(tmpMat); + if (transform) { + tmpRect.copy(childRect); + tmpRect.applyTransform(transform); + rect = rect || tmpRect.clone(); + rect.union(tmpRect); + } + else { + rect = rect || childRect.clone(); + rect.union(childRect); + } + } + return rect || tmpRect; + } + }; + + zrUtil.inherits(Group, Element); + + module.exports = Group; + + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module zrender/Element + */ + + + var guid = __webpack_require__(32); + var Eventful = __webpack_require__(33); + var Transformable = __webpack_require__(34); + var Animatable = __webpack_require__(35); + var zrUtil = __webpack_require__(4); + + /** + * @alias module:zrender/Element + * @constructor + * @extends {module:zrender/mixin/Animatable} + * @extends {module:zrender/mixin/Transformable} + * @extends {module:zrender/mixin/Eventful} + */ + var Element = function (opts) { + + Transformable.call(this, opts); + Eventful.call(this, opts); + Animatable.call(this, opts); + + /** + * 画布元素ID + * @type {string} + */ + this.id = opts.id || guid(); + }; + + Element.prototype = { + + /** + * 元素类型 + * Element type + * @type {string} + */ + type: 'element', + + /** + * 元素名字 + * Element name + * @type {string} + */ + name: '', + + /** + * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值 + * ZRender instance will be assigned when element is associated with zrender + * @name module:/zrender/Element#__zr + * @type {module:zrender/ZRender} + */ + __zr: null, + + /** + * 图形是否忽略,为true时忽略图形的绘制以及事件触发 + * If ignore drawing and events of the element object + * @name module:/zrender/Element#ignore + * @type {boolean} + * @default false + */ + ignore: false, + + /** + * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪 + * 该路径会继承被裁减对象的变换 + * @type {module:zrender/graphic/Path} + * @see http://www.w3.org/TR/2dcontext/#clipping-region + * @readOnly + */ + clipPath: null, + + /** + * Drift element + * @param {number} dx dx on the global space + * @param {number} dy dy on the global space + */ + drift: function (dx, dy) { + switch (this.draggable) { + case 'horizontal': + dy = 0; + break; + case 'vertical': + dx = 0; + break; + } + + var m = this.transform; + if (!m) { + m = this.transform = [1, 0, 0, 1, 0, 0]; + } + m[4] += dx; + m[5] += dy; + + this.decomposeTransform(); + this.dirty(false); + }, + + /** + * Hook before update + */ + beforeUpdate: function () {}, + /** + * Hook after update + */ + afterUpdate: function () {}, + /** + * Update each frame + */ + update: function () { + this.updateTransform(); + }, + + /** + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) {}, + + /** + * @protected + */ + attrKV: function (key, value) { + if (key === 'position' || key === 'scale' || key === 'origin') { + // Copy the array + if (value) { + var target = this[key]; + if (!target) { + target = this[key] = []; + } + target[0] = value[0]; + target[1] = value[1]; + } + } + else { + this[key] = value; + } + }, + + /** + * Hide the element + */ + hide: function () { + this.ignore = true; + this.__zr && this.__zr.refresh(); + }, + + /** + * Show the element + */ + show: function () { + this.ignore = false; + this.__zr && this.__zr.refresh(); + }, + + /** + * @param {string|Object} key + * @param {*} value + */ + attr: function (key, value) { + if (typeof key === 'string') { + this.attrKV(key, value); + } + else if (zrUtil.isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.attrKV(name, key[name]); + } + } + } + + this.dirty(false); + + return this; + }, + + /** + * @param {module:zrender/graphic/Path} clipPath + */ + setClipPath: function (clipPath) { + var zr = this.__zr; + if (zr) { + clipPath.addSelfToZr(zr); + } + + // Remove previous clip path + if (this.clipPath && this.clipPath !== clipPath) { + this.removeClipPath(); + } + + this.clipPath = clipPath; + clipPath.__zr = zr; + clipPath.__clipTarget = this; + + this.dirty(false); + }, + + /** + */ + removeClipPath: function () { + var clipPath = this.clipPath; + if (clipPath) { + if (clipPath.__zr) { + clipPath.removeSelfFromZr(clipPath.__zr); + } + + clipPath.__zr = null; + clipPath.__clipTarget = null; + this.clipPath = null; + + this.dirty(false); + } + }, + + /** + * Add self from zrender instance. + * Not recursively because it will be invoked when element added to storage. + * @param {module:zrender/ZRender} zr + */ + addSelfToZr: function (zr) { + this.__zr = zr; + // 添加动画 + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.addAnimator(animators[i]); + } + } + + if (this.clipPath) { + this.clipPath.addSelfToZr(zr); + } + }, + + /** + * Remove self from zrender instance. + * Not recursively because it will be invoked when element added to storage. + * @param {module:zrender/ZRender} zr + */ + removeSelfFromZr: function (zr) { + this.__zr = null; + // 移除动画 + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.removeAnimator(animators[i]); + } + } + + if (this.clipPath) { + this.clipPath.removeSelfFromZr(zr); + } + } + }; + + zrUtil.mixin(Element, Animatable); + zrUtil.mixin(Element, Transformable); + zrUtil.mixin(Element, Eventful); + + module.exports = Element; + + +/***/ }, +/* 32 */ +/***/ function(module, exports) { + + /** + * zrender: 生成唯一id + * + * @author errorrik (errorrik@gmail.com) + */ + + + var idStart = 0x0907; + + module.exports = function () { + return idStart++; + }; + + + +/***/ }, +/* 33 */ +/***/ function(module, exports) { + + /** + * 事件扩展 + * @module zrender/mixin/Eventful + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * pissang (https://www.github.com/pissang) + */ + + + var arrySlice = Array.prototype.slice; + + /** + * 事件分发器 + * @alias module:zrender/mixin/Eventful + * @constructor + */ + var Eventful = function () { + this._$handlers = {}; + }; + + Eventful.prototype = { + + constructor: Eventful, + + /** + * 单次触发绑定,trigger后销毁 + * + * @param {string} event 事件名 + * @param {Function} handler 响应函数 + * @param {Object} context + */ + one: function (event, handler, context) { + var _h = this._$handlers; + + if (!handler || !event) { + return this; + } + + if (!_h[event]) { + _h[event] = []; + } + + for (var i = 0; i < _h[event].length; i++) { + if (_h[event][i].h === handler) { + return this; + } + } + + _h[event].push({ + h: handler, + one: true, + ctx: context || this + }); + + return this; + }, + + /** + * 绑定事件 + * @param {string} event 事件名 + * @param {Function} handler 事件处理函数 + * @param {Object} [context] + */ + on: function (event, handler, context) { + var _h = this._$handlers; + + if (!handler || !event) { + return this; + } + + if (!_h[event]) { + _h[event] = []; + } + + for (var i = 0; i < _h[event].length; i++) { + if (_h[event][i].h === handler) { + return this; + } + } + + _h[event].push({ + h: handler, + one: false, + ctx: context || this + }); + + return this; + }, + + /** + * 是否绑定了事件 + * @param {string} event + * @return {boolean} + */ + isSilent: function (event) { + var _h = this._$handlers; + return _h[event] && _h[event].length; + }, + + /** + * 解绑事件 + * @param {string} event 事件名 + * @param {Function} [handler] 事件处理函数 + */ + off: function (event, handler) { + var _h = this._$handlers; + + if (!event) { + this._$handlers = {}; + return this; + } + + if (handler) { + if (_h[event]) { + var newList = []; + for (var i = 0, l = _h[event].length; i < l; i++) { + if (_h[event][i]['h'] != handler) { + newList.push(_h[event][i]); + } + } + _h[event] = newList; + } + + if (_h[event] && _h[event].length === 0) { + delete _h[event]; + } + } + else { + delete _h[event]; + } + + return this; + }, + + /** + * 事件分发 + * + * @param {string} type 事件类型 + */ + trigger: function (type) { + if (this._$handlers[type]) { + var args = arguments; + var argLen = args.length; + + if (argLen > 3) { + args = arrySlice.call(args, 1); + } + + var _h = this._$handlers[type]; + var len = _h.length; + for (var i = 0; i < len;) { + // Optimize advise from backbone + switch (argLen) { + case 1: + _h[i]['h'].call(_h[i]['ctx']); + break; + case 2: + _h[i]['h'].call(_h[i]['ctx'], args[1]); + break; + case 3: + _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]); + break; + default: + // have more than 2 given arguments + _h[i]['h'].apply(_h[i]['ctx'], args); + break; + } + + if (_h[i]['one']) { + _h.splice(i, 1); + len--; + } + else { + i++; + } + } + } + + return this; + }, + + /** + * 带有context的事件分发, 最后一个参数是事件回调的context + * @param {string} type 事件类型 + */ + triggerWithContext: function (type) { + if (this._$handlers[type]) { + var args = arguments; + var argLen = args.length; + + if (argLen > 4) { + args = arrySlice.call(args, 1, args.length - 1); + } + var ctx = args[args.length - 1]; + + var _h = this._$handlers[type]; + var len = _h.length; + for (var i = 0; i < len;) { + // Optimize advise from backbone + switch (argLen) { + case 1: + _h[i]['h'].call(ctx); + break; + case 2: + _h[i]['h'].call(ctx, args[1]); + break; + case 3: + _h[i]['h'].call(ctx, args[1], args[2]); + break; + default: + // have more than 2 given arguments + _h[i]['h'].apply(ctx, args); + break; + } + + if (_h[i]['one']) { + _h.splice(i, 1); + len--; + } + else { + i++; + } + } + } + + return this; + } + }; + + // 对象可以通过 onxxxx 绑定事件 + /** + * @event module:zrender/mixin/Eventful#onclick + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseover + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseout + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousemove + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousewheel + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousedown + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseup + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragstart + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragend + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragenter + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragleave + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragover + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondrop + * @type {Function} + * @default null + */ + + module.exports = Eventful; + + + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 提供变换扩展 + * @module zrender/mixin/Transformable + * @author pissang (https://www.github.com/pissang) + */ + + + var matrix = __webpack_require__(11); + var vector = __webpack_require__(10); + var mIdentity = matrix.identity; + + var EPSILON = 5e-5; + + function isNotAroundZero(val) { + return val > EPSILON || val < -EPSILON; + } + + /** + * @alias module:zrender/mixin/Transformable + * @constructor + */ + var Transformable = function (opts) { + opts = opts || {}; + // If there are no given position, rotation, scale + if (!opts.position) { + /** + * 平移 + * @type {Array.} + * @default [0, 0] + */ + this.position = [0, 0]; + } + if (opts.rotation == null) { + /** + * 旋转 + * @type {Array.} + * @default 0 + */ + this.rotation = 0; + } + if (!opts.scale) { + /** + * 缩放 + * @type {Array.} + * @default [1, 1] + */ + this.scale = [1, 1]; + } + /** + * 旋转和缩放的原点 + * @type {Array.} + * @default null + */ + this.origin = this.origin || null; + }; + + var transformableProto = Transformable.prototype; + transformableProto.transform = null; + + /** + * 判断是否需要有坐标变换 + * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵 + */ + transformableProto.needLocalTransform = function () { + return isNotAroundZero(this.rotation) + || isNotAroundZero(this.position[0]) + || isNotAroundZero(this.position[1]) + || isNotAroundZero(this.scale[0] - 1) + || isNotAroundZero(this.scale[1] - 1); + }; + + transformableProto.updateTransform = function () { + var parent = this.parent; + var parentHasTransform = parent && parent.transform; + var needLocalTransform = this.needLocalTransform(); + + var m = this.transform; + if (!(needLocalTransform || parentHasTransform)) { + m && mIdentity(m); + return; + } + + m = m || matrix.create(); + + if (needLocalTransform) { + this.getLocalTransform(m); + } + else { + mIdentity(m); + } + + // 应用父节点变换 + if (parentHasTransform) { + if (needLocalTransform) { + matrix.mul(m, parent.transform, m); + } + else { + matrix.copy(m, parent.transform); + } + } + // 保存这个变换矩阵 + this.transform = m; + + this.invTransform = this.invTransform || matrix.create(); + matrix.invert(this.invTransform, m); + }; + + transformableProto.getLocalTransform = function (m) { + m = m || []; + mIdentity(m); + + var origin = this.origin; + + var scale = this.scale; + var rotation = this.rotation; + var position = this.position; + if (origin) { + // Translate to origin + m[4] -= origin[0]; + m[5] -= origin[1]; + } + matrix.scale(m, m, scale); + if (rotation) { + matrix.rotate(m, m, rotation); + } + if (origin) { + // Translate back from origin + m[4] += origin[0]; + m[5] += origin[1]; + } + + m[4] += position[0]; + m[5] += position[1]; + + return m; + }; + /** + * 将自己的transform应用到context上 + * @param {Context2D} ctx + */ + transformableProto.setTransform = function (ctx) { + var m = this.transform; + var dpr = ctx.dpr || 1; + if (m) { + ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]); + } + else { + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + }; + + transformableProto.restoreTransform = function (ctx) { + var m = this.transform; + var dpr = ctx.dpr || 1; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + var tmpTransform = []; + + /** + * 分解`transform`矩阵到`position`, `rotation`, `scale` + */ + transformableProto.decomposeTransform = function () { + if (!this.transform) { + return; + } + var parent = this.parent; + var m = this.transform; + if (parent && parent.transform) { + // Get local transform and decompose them to position, scale, rotation + matrix.mul(tmpTransform, parent.invTransform, m); + m = tmpTransform; + } + var sx = m[0] * m[0] + m[1] * m[1]; + var sy = m[2] * m[2] + m[3] * m[3]; + var position = this.position; + var scale = this.scale; + if (isNotAroundZero(sx - 1)) { + sx = Math.sqrt(sx); + } + if (isNotAroundZero(sy - 1)) { + sy = Math.sqrt(sy); + } + if (m[0] < 0) { + sx = -sx; + } + if (m[3] < 0) { + sy = -sy; + } + position[0] = m[4]; + position[1] = m[5]; + scale[0] = sx; + scale[1] = sy; + this.rotation = Math.atan2(-m[1] / sy, m[0] / sx); + }; + + /** + * Get global scale + * @return {Array.} + */ + transformableProto.getGlobalScale = function () { + var m = this.transform; + if (!m) { + return [1, 1]; + } + var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]); + var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]); + if (m[0] < 0) { + sx = -sx; + } + if (m[3] < 0) { + sy = -sy; + } + return [sx, sy]; + }; + /** + * 变换坐标位置到 shape 的局部坐标空间 + * @method + * @param {number} x + * @param {number} y + * @return {Array.} + */ + transformableProto.transformCoordToLocal = function (x, y) { + var v2 = [x, y]; + var invTransform = this.invTransform; + if (invTransform) { + vector.applyTransform(v2, v2, invTransform); + } + return v2; + }; + + /** + * 变换局部坐标位置到全局坐标空间 + * @method + * @param {number} x + * @param {number} y + * @return {Array.} + */ + transformableProto.transformCoordToGlobal = function (x, y) { + var v2 = [x, y]; + var transform = this.transform; + if (transform) { + vector.applyTransform(v2, v2, transform); + } + return v2; + }; + + module.exports = Transformable; + + + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module zrender/mixin/Animatable + */ + + + var Animator = __webpack_require__(36); + var util = __webpack_require__(4); + var isString = util.isString; + var isFunction = util.isFunction; + var isObject = util.isObject; + var log = __webpack_require__(40); + + /** + * @alias modue:zrender/mixin/Animatable + * @constructor + */ + var Animatable = function () { + + /** + * @type {Array.} + * @readOnly + */ + this.animators = []; + }; + + Animatable.prototype = { + + constructor: Animatable, + + /** + * 动画 + * + * @param {string} path 需要添加动画的属性获取路径,可以通过a.b.c来获取深层的属性 + * @param {boolean} [loop] 动画是否循环 + * @return {module:zrender/animation/Animator} + * @example: + * el.animate('style', false) + * .when(1000, {x: 10} ) + * .done(function(){ // Animation done }) + * .start() + */ + animate: function (path, loop) { + var target; + var animatingShape = false; + var el = this; + var zr = this.__zr; + if (path) { + var pathSplitted = path.split('.'); + var prop = el; + // If animating shape + animatingShape = pathSplitted[0] === 'shape'; + for (var i = 0, l = pathSplitted.length; i < l; i++) { + if (!prop) { + continue; + } + prop = prop[pathSplitted[i]]; + } + if (prop) { + target = prop; + } + } + else { + target = el; + } + + if (!target) { + log( + 'Property "' + + path + + '" is not existed in element ' + + el.id + ); + return; + } + + var animators = el.animators; + + var animator = new Animator(target, loop); + + animator.during(function (target) { + el.dirty(animatingShape); + }) + .done(function () { + // FIXME Animator will not be removed if use `Animator#stop` to stop animation + animators.splice(util.indexOf(animators, animator), 1); + }); + + animators.push(animator); + + // If animate after added to the zrender + if (zr) { + zr.animation.addAnimator(animator); + } + + return animator; + }, + + /** + * 停止动画 + * @param {boolean} forwardToLast If move to last frame before stop + */ + stopAnimation: function (forwardToLast) { + var animators = this.animators; + var len = animators.length; + for (var i = 0; i < len; i++) { + animators[i].stop(forwardToLast); + } + animators.length = 0; + + return this; + }, + + /** + * @param {Object} target + * @param {number} [time=500] Time in ms + * @param {string} [easing='linear'] + * @param {number} [delay=0] + * @param {Function} [callback] + * + * @example + * // Animate position + * el.animateTo({ + * position: [10, 10] + * }, function () { // done }) + * + * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing + * el.animateTo({ + * shape: { + * width: 500 + * }, + * style: { + * fill: 'red' + * } + * position: [10, 10] + * }, 100, 100, 'cubicOut', function () { // done }) + */ + // TODO Return animation key + animateTo: function (target, time, delay, easing, callback) { + // animateTo(target, time, easing, callback); + if (isString(delay)) { + callback = easing; + easing = delay; + delay = 0; + } + // animateTo(target, time, delay, callback); + else if (isFunction(easing)) { + callback = easing; + easing = 'linear'; + delay = 0; + } + // animateTo(target, time, callback); + else if (isFunction(delay)) { + callback = delay; + delay = 0; + } + // animateTo(target, callback) + else if (isFunction(time)) { + callback = time; + time = 500; + } + // animateTo(target) + else if (!time) { + time = 500; + } + // Stop all previous animations + this.stopAnimation(); + this._animateToShallow('', this, target, time, delay, easing, callback); + + // Animators may be removed immediately after start + // if there is nothing to animate + var animators = this.animators.slice(); + var count = animators.length; + function done() { + count--; + if (!count) { + callback && callback(); + } + } + + // No animators. This should be checked before animators[i].start(), + // because 'done' may be executed immediately if no need to animate. + if (!count) { + callback && callback(); + } + // Start after all animators created + // Incase any animator is done immediately when all animation properties are not changed + for (var i = 0; i < animators.length; i++) { + animators[i] + .done(done) + .start(easing); + } + }, + + /** + * @private + * @param {string} path='' + * @param {Object} source=this + * @param {Object} target + * @param {number} [time=500] + * @param {number} [delay=0] + * + * @example + * // Animate position + * el._animateToShallow({ + * position: [10, 10] + * }) + * + * // Animate shape, style and position in 100ms, delayed 100ms + * el._animateToShallow({ + * shape: { + * width: 500 + * }, + * style: { + * fill: 'red' + * } + * position: [10, 10] + * }, 100, 100) + */ + _animateToShallow: function (path, source, target, time, delay) { + var objShallow = {}; + var propertyCount = 0; + for (var name in target) { + if (source[name] != null) { + if (isObject(target[name]) && !util.isArrayLike(target[name])) { + this._animateToShallow( + path ? path + '.' + name : name, + source[name], + target[name], + time, + delay + ); + } + else { + objShallow[name] = target[name]; + propertyCount++; + } + } + else if (target[name] != null) { + // Attr directly if not has property + // FIXME, if some property not needed for element ? + if (!path) { + this.attr(name, target[name]); + } + else { // Shape or style + var props = {}; + props[path] = {}; + props[path][name] = target[name]; + this.attr(props); + } + } + } + + if (propertyCount > 0) { + this.animate(path, false) + .when(time == null ? 500 : time, objShallow) + .delay(delay || 0); + } + + return this; + } + }; + + module.exports = Animatable; + + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/animation/Animator + */ + + + var Clip = __webpack_require__(37); + var color = __webpack_require__(39); + var util = __webpack_require__(4); + var isArrayLike = util.isArrayLike; + + var arraySlice = Array.prototype.slice; + + function defaultGetter(target, key) { + return target[key]; + } + + function defaultSetter(target, key, value) { + target[key] = value; + } + + /** + * @param {number} p0 + * @param {number} p1 + * @param {number} percent + * @return {number} + */ + function interpolateNumber(p0, p1, percent) { + return (p1 - p0) * percent + p0; + } + + /** + * @param {string} p0 + * @param {string} p1 + * @param {number} percent + * @return {string} + */ + function interpolateString(p0, p1, percent) { + return percent > 0.5 ? p1 : p0; + } + + /** + * @param {Array} p0 + * @param {Array} p1 + * @param {number} percent + * @param {Array} out + * @param {number} arrDim + */ + function interpolateArray(p0, p1, percent, out, arrDim) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = interpolateNumber(p0[i], p1[i], percent); + } + } + else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = interpolateNumber( + p0[i][j], p1[i][j], percent + ); + } + } + } + } + + // arr0 is source array, arr1 is target array. + // Do some preprocess to avoid error happened when interpolating from arr0 to arr1 + function fillArr(arr0, arr1, arrDim) { + var arr0Len = arr0.length; + var arr1Len = arr1.length; + if (arr0Len !== arr1Len) { + // FIXME Not work for TypedArray + var isPreviousLarger = arr0Len > arr1Len; + if (isPreviousLarger) { + // Cut the previous + arr0.length = arr1Len; + } + else { + // Fill the previous + for (var i = arr0Len; i < arr1Len; i++) { + arr0.push( + arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]) + ); + } + } + } + // Handling NaN value + var len2 = arr0[0] && arr0[0].length; + for (var i = 0; i < arr0.length; i++) { + if (arrDim === 1) { + if (isNaN(arr0[i])) { + arr0[i] = arr1[i]; + } + } + else { + for (var j = 0; j < len2; j++) { + if (isNaN(arr0[i][j])) { + arr0[i][j] = arr1[i][j]; + } + } + } + } + } + + /** + * @param {Array} arr0 + * @param {Array} arr1 + * @param {number} arrDim + * @return {boolean} + */ + function isArraySame(arr0, arr1, arrDim) { + if (arr0 === arr1) { + return true; + } + var len = arr0.length; + if (len !== arr1.length) { + return false; + } + if (arrDim === 1) { + for (var i = 0; i < len; i++) { + if (arr0[i] !== arr1[i]) { + return false; + } + } + } + else { + var len2 = arr0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + if (arr0[i][j] !== arr1[i][j]) { + return false; + } + } + } + } + return true; + } + + /** + * Catmull Rom interpolate array + * @param {Array} p0 + * @param {Array} p1 + * @param {Array} p2 + * @param {Array} p3 + * @param {number} t + * @param {number} t2 + * @param {number} t3 + * @param {Array} out + * @param {number} arrDim + */ + function catmullRomInterpolateArray( + p0, p1, p2, p3, t, t2, t3, out, arrDim + ) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = catmullRomInterpolate( + p0[i], p1[i], p2[i], p3[i], t, t2, t3 + ); + } + } + else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = catmullRomInterpolate( + p0[i][j], p1[i][j], p2[i][j], p3[i][j], + t, t2, t3 + ); + } + } + } + } + + /** + * Catmull Rom interpolate number + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @param {number} t2 + * @param {number} t3 + * @return {number} + */ + function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + return (2 * (p1 - p2) + v0 + v1) * t3 + + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + + v0 * t + p1; + } + + function cloneValue(value) { + if (isArrayLike(value)) { + var len = value.length; + if (isArrayLike(value[0])) { + var ret = []; + for (var i = 0; i < len; i++) { + ret.push(arraySlice.call(value[i])); + } + return ret; + } + + return arraySlice.call(value); + } + + return value; + } + + function rgba2String(rgba) { + rgba[0] = Math.floor(rgba[0]); + rgba[1] = Math.floor(rgba[1]); + rgba[2] = Math.floor(rgba[2]); + + return 'rgba(' + rgba.join(',') + ')'; + } + + function createTrackClip (animator, easing, oneTrackDone, keyframes, propName) { + var getter = animator._getter; + var setter = animator._setter; + var useSpline = easing === 'spline'; + + var trackLen = keyframes.length; + if (!trackLen) { + return; + } + // Guess data type + var firstVal = keyframes[0].value; + var isValueArray = isArrayLike(firstVal); + var isValueColor = false; + var isValueString = false; + + // For vertices morphing + var arrDim = ( + isValueArray + && isArrayLike(firstVal[0]) + ) + ? 2 : 1; + var trackMaxTime; + // Sort keyframe as ascending + keyframes.sort(function(a, b) { + return a.time - b.time; + }); + + trackMaxTime = keyframes[trackLen - 1].time; + // Percents of each keyframe + var kfPercents = []; + // Value of each keyframe + var kfValues = []; + var prevValue = keyframes[0].value; + var isAllValueEqual = true; + for (var i = 0; i < trackLen; i++) { + kfPercents.push(keyframes[i].time / trackMaxTime); + // Assume value is a color when it is a string + var value = keyframes[i].value; + + // Check if value is equal, deep check if value is array + if (!((isValueArray && isArraySame(value, prevValue, arrDim)) + || (!isValueArray && value === prevValue))) { + isAllValueEqual = false; + } + prevValue = value; + + // Try converting a string to a color array + if (typeof value == 'string') { + var colorArray = color.parse(value); + if (colorArray) { + value = colorArray; + isValueColor = true; + } + else { + isValueString = true; + } + } + kfValues.push(value); + } + if (isAllValueEqual) { + return; + } + + var lastValue = kfValues[trackLen - 1]; + // Polyfill array and NaN value + for (var i = 0; i < trackLen - 1; i++) { + if (isValueArray) { + fillArr(kfValues[i], lastValue, arrDim); + } + else { + if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) { + kfValues[i] = lastValue; + } + } + } + isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim); + + // Cache the key of last frame to speed up when + // animation playback is sequency + var lastFrame = 0; + var lastFramePercent = 0; + var start; + var w; + var p0; + var p1; + var p2; + var p3; + + if (isValueColor) { + var rgba = [0, 0, 0, 0]; + } + + var onframe = function (target, percent) { + // Find the range keyframes + // kf1-----kf2---------current--------kf3 + // find kf2 and kf3 and do interpolation + var frame; + // In the easing function like elasticOut, percent may less than 0 + if (percent < 0) { + frame = 0; + } + else if (percent < lastFramePercent) { + // Start from next key + // PENDING start from lastFrame ? + start = Math.min(lastFrame + 1, trackLen - 1); + for (frame = start; frame >= 0; frame--) { + if (kfPercents[frame] <= percent) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, trackLen - 2); + } + else { + for (frame = lastFrame; frame < trackLen; frame++) { + if (kfPercents[frame] > percent) { + break; + } + } + frame = Math.min(frame - 1, trackLen - 2); + } + lastFrame = frame; + lastFramePercent = percent; + + var range = (kfPercents[frame + 1] - kfPercents[frame]); + if (range === 0) { + return; + } + else { + w = (percent - kfPercents[frame]) / range; + } + if (useSpline) { + p1 = kfValues[frame]; + p0 = kfValues[frame === 0 ? frame : frame - 1]; + p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1]; + p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2]; + if (isValueArray) { + catmullRomInterpolateArray( + p0, p1, p2, p3, w, w * w, w * w * w, + getter(target, propName), + arrDim + ); + } + else { + var value; + if (isValueColor) { + value = catmullRomInterpolateArray( + p0, p1, p2, p3, w, w * w, w * w * w, + rgba, 1 + ); + value = rgba2String(rgba); + } + else if (isValueString) { + // String is step(0.5) + return interpolateString(p1, p2, w); + } + else { + value = catmullRomInterpolate( + p0, p1, p2, p3, w, w * w, w * w * w + ); + } + setter( + target, + propName, + value + ); + } + } + else { + if (isValueArray) { + interpolateArray( + kfValues[frame], kfValues[frame + 1], w, + getter(target, propName), + arrDim + ); + } + else { + var value; + if (isValueColor) { + interpolateArray( + kfValues[frame], kfValues[frame + 1], w, + rgba, 1 + ); + value = rgba2String(rgba); + } + else if (isValueString) { + // String is step(0.5) + return interpolateString(kfValues[frame], kfValues[frame + 1], w); + } + else { + value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w); + } + setter( + target, + propName, + value + ); + } + } + }; + + var clip = new Clip({ + target: animator._target, + life: trackMaxTime, + loop: animator._loop, + delay: animator._delay, + onframe: onframe, + ondestroy: oneTrackDone + }); + + if (easing && easing !== 'spline') { + clip.easing = easing; + } + + return clip; + } + + /** + * @alias module:zrender/animation/Animator + * @constructor + * @param {Object} target + * @param {boolean} loop + * @param {Function} getter + * @param {Function} setter + */ + var Animator = function(target, loop, getter, setter) { + this._tracks = {}; + this._target = target; + + this._loop = loop || false; + + this._getter = getter || defaultGetter; + this._setter = setter || defaultSetter; + + this._clipCount = 0; + + this._delay = 0; + + this._doneList = []; + + this._onframeList = []; + + this._clipList = []; + }; + + Animator.prototype = { + /** + * 设置动画关键帧 + * @param {number} time 关键帧时间,单位是ms + * @param {Object} props 关键帧的属性值,key-value表示 + * @return {module:zrender/animation/Animator} + */ + when: function(time /* ms */, props) { + var tracks = this._tracks; + for (var propName in props) { + if (!tracks[propName]) { + tracks[propName] = []; + // Invalid value + var value = this._getter(this._target, propName); + if (value == null) { + // zrLog('Invalid property ' + propName); + continue; + } + // If time is 0 + // Then props is given initialize value + // Else + // Initialize value from current prop value + if (time !== 0) { + tracks[propName].push({ + time: 0, + value: cloneValue(value) + }); + } + } + tracks[propName].push({ + time: time, + value: props[propName] + }); + } + return this; + }, + /** + * 添加动画每一帧的回调函数 + * @param {Function} callback + * @return {module:zrender/animation/Animator} + */ + during: function (callback) { + this._onframeList.push(callback); + return this; + }, + + _doneCallback: function () { + // Clear all tracks + this._tracks = {}; + // Clear all clips + this._clipList.length = 0; + + var doneList = this._doneList; + var len = doneList.length; + for (var i = 0; i < len; i++) { + doneList[i].call(this); + } + }, + /** + * 开始执行动画 + * @param {string|Function} easing + * 动画缓动函数,详见{@link module:zrender/animation/easing} + * @return {module:zrender/animation/Animator} + */ + start: function (easing) { + + var self = this; + var clipCount = 0; + + var oneTrackDone = function() { + clipCount--; + if (!clipCount) { + self._doneCallback(); + } + }; + + var lastClip; + for (var propName in this._tracks) { + var clip = createTrackClip( + this, easing, oneTrackDone, + this._tracks[propName], propName + ); + if (clip) { + this._clipList.push(clip); + clipCount++; + + // If start after added to animation + if (this.animation) { + this.animation.addClip(clip); + } + + lastClip = clip; + } + } + + // Add during callback on the last clip + if (lastClip) { + var oldOnFrame = lastClip.onframe; + lastClip.onframe = function (target, percent) { + oldOnFrame(target, percent); + + for (var i = 0; i < self._onframeList.length; i++) { + self._onframeList[i](target, percent); + } + }; + } + + if (!clipCount) { + this._doneCallback(); + } + return this; + }, + /** + * 停止动画 + * @param {boolean} forwardToLast If move to last frame before stop + */ + stop: function (forwardToLast) { + var clipList = this._clipList; + var animation = this.animation; + for (var i = 0; i < clipList.length; i++) { + var clip = clipList[i]; + if (forwardToLast) { + // Move to last frame before stop + clip.onframe(this._target, 1); + } + animation && animation.removeClip(clip); + } + clipList.length = 0; + }, + /** + * 设置动画延迟开始的时间 + * @param {number} time 单位ms + * @return {module:zrender/animation/Animator} + */ + delay: function (time) { + this._delay = time; + return this; + }, + /** + * 添加动画结束的回调 + * @param {Function} cb + * @return {module:zrender/animation/Animator} + */ + done: function(cb) { + if (cb) { + this._doneList.push(cb); + } + return this; + }, + + /** + * @return {Array.} + */ + getClips: function () { + return this._clipList; + } + }; + + module.exports = Animator; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 动画主控制器 + * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件 + * @config life(1000) 动画时长 + * @config delay(0) 动画延迟时间 + * @config loop(true) + * @config gap(0) 循环的间隔时间 + * @config onframe + * @config easing(optional) + * @config ondestroy(optional) + * @config onrestart(optional) + * + * TODO pause + */ + + + var easingFuncs = __webpack_require__(38); + + function Clip(options) { + + this._target = options.target; + + // 生命周期 + this._life = options.life || 1000; + // 延时 + this._delay = options.delay || 0; + // 开始时间 + // this._startTime = new Date().getTime() + this._delay;// 单位毫秒 + this._initialized = false; + + // 是否循环 + this.loop = options.loop == null ? false : options.loop; + + this.gap = options.gap || 0; + + this.easing = options.easing || 'Linear'; + + this.onframe = options.onframe; + this.ondestroy = options.ondestroy; + this.onrestart = options.onrestart; + } + + Clip.prototype = { + + constructor: Clip, + + step: function (globalTime) { + // Set startTime on first step, or _startTime may has milleseconds different between clips + // PENDING + if (!this._initialized) { + this._startTime = globalTime + this._delay; + this._initialized = true; + } + + var percent = (globalTime - this._startTime) / this._life; + + // 还没开始 + if (percent < 0) { + return; + } + + percent = Math.min(percent, 1); + + var easing = this.easing; + var easingFunc = typeof easing == 'string' ? easingFuncs[easing] : easing; + var schedule = typeof easingFunc === 'function' + ? easingFunc(percent) + : percent; + + this.fire('frame', schedule); + + // 结束 + if (percent == 1) { + if (this.loop) { + this.restart (globalTime); + // 重新开始周期 + // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件 + return 'restart'; + } + + // 动画完成将这个控制器标识为待删除 + // 在Animation.update中进行批量删除 + this._needsRemove = true; + return 'destroy'; + } + + return null; + }, + + restart: function (globalTime) { + var remainder = (globalTime - this._startTime) % this._life; + this._startTime = globalTime - remainder + this.gap; + + this._needsRemove = false; + }, + + fire: function(eventType, arg) { + eventType = 'on' + eventType; + if (this[eventType]) { + this[eventType](this._target, arg); + } + } + }; + + module.exports = Clip; + + + +/***/ }, +/* 38 */ +/***/ function(module, exports) { + + /** + * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js + * @see http://sole.github.io/tween.js/examples/03_graphs.html + * @exports zrender/animation/easing + */ + + var easing = { + /** + * @param {number} k + * @return {number} + */ + linear: function (k) { + return k; + }, + + /** + * @param {number} k + * @return {number} + */ + quadraticIn: function (k) { + return k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quadraticOut: function (k) { + return k * (2 - k); + }, + /** + * @param {number} k + * @return {number} + */ + quadraticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + return -0.5 * (--k * (k - 2) - 1); + }, + + // 三次方的缓动(t^3) + /** + * @param {number} k + * @return {number} + */ + cubicIn: function (k) { + return k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + cubicOut: function (k) { + return --k * k * k + 1; + }, + /** + * @param {number} k + * @return {number} + */ + cubicInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + return 0.5 * ((k -= 2) * k * k + 2); + }, + + // 四次方的缓动(t^4) + /** + * @param {number} k + * @return {number} + */ + quarticIn: function (k) { + return k * k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quarticOut: function (k) { + return 1 - (--k * k * k * k); + }, + /** + * @param {number} k + * @return {number} + */ + quarticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + return -0.5 * ((k -= 2) * k * k * k - 2); + }, + + // 五次方的缓动(t^5) + /** + * @param {number} k + * @return {number} + */ + quinticIn: function (k) { + return k * k * k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quinticOut: function (k) { + return --k * k * k * k * k + 1; + }, + /** + * @param {number} k + * @return {number} + */ + quinticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k * k; + } + return 0.5 * ((k -= 2) * k * k * k * k + 2); + }, + + // 正弦曲线的缓动(sin(t)) + /** + * @param {number} k + * @return {number} + */ + sinusoidalIn: function (k) { + return 1 - Math.cos(k * Math.PI / 2); + }, + /** + * @param {number} k + * @return {number} + */ + sinusoidalOut: function (k) { + return Math.sin(k * Math.PI / 2); + }, + /** + * @param {number} k + * @return {number} + */ + sinusoidalInOut: function (k) { + return 0.5 * (1 - Math.cos(Math.PI * k)); + }, + + // 指数曲线的缓动(2^t) + /** + * @param {number} k + * @return {number} + */ + exponentialIn: function (k) { + return k === 0 ? 0 : Math.pow(1024, k - 1); + }, + /** + * @param {number} k + * @return {number} + */ + exponentialOut: function (k) { + return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); + }, + /** + * @param {number} k + * @return {number} + */ + exponentialInOut: function (k) { + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if ((k *= 2) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2); + }, + + // 圆形曲线的缓动(sqrt(1-t^2)) + /** + * @param {number} k + * @return {number} + */ + circularIn: function (k) { + return 1 - Math.sqrt(1 - k * k); + }, + /** + * @param {number} k + * @return {number} + */ + circularOut: function (k) { + return Math.sqrt(1 - (--k * k)); + }, + /** + * @param {number} k + * @return {number} + */ + circularInOut: function (k) { + if ((k *= 2) < 1) { + return -0.5 * (Math.sqrt(1 - k * k) - 1); + } + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); + }, + + // 创建类似于弹簧在停止前来回振荡的动画 + /** + * @param {number} k + * @return {number} + */ + elasticIn: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return -(a * Math.pow(2, 10 * (k -= 1)) * + Math.sin((k - s) * (2 * Math.PI) / p)); + }, + /** + * @param {number} k + * @return {number} + */ + elasticOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return (a * Math.pow(2, -10 * k) * + Math.sin((k - s) * (2 * Math.PI) / p) + 1); + }, + /** + * @param {number} k + * @return {number} + */ + elasticInOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + if ((k *= 2) < 1) { + return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; + + }, + + // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动 + /** + * @param {number} k + * @return {number} + */ + backIn: function (k) { + var s = 1.70158; + return k * k * ((s + 1) * k - s); + }, + /** + * @param {number} k + * @return {number} + */ + backOut: function (k) { + var s = 1.70158; + return --k * k * ((s + 1) * k + s) + 1; + }, + /** + * @param {number} k + * @return {number} + */ + backInOut: function (k) { + var s = 1.70158 * 1.525; + if ((k *= 2) < 1) { + return 0.5 * (k * k * ((s + 1) * k - s)); + } + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); + }, + + // 创建弹跳效果 + /** + * @param {number} k + * @return {number} + */ + bounceIn: function (k) { + return 1 - easing.bounceOut(1 - k); + }, + /** + * @param {number} k + * @return {number} + */ + bounceOut: function (k) { + if (k < (1 / 2.75)) { + return 7.5625 * k * k; + } + else if (k < (2 / 2.75)) { + return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; + } + else if (k < (2.5 / 2.75)) { + return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; + } + else { + return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; + } + }, + /** + * @param {number} k + * @return {number} + */ + bounceInOut: function (k) { + if (k < 0.5) { + return easing.bounceIn(k * 2) * 0.5; + } + return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; + } + }; + + module.exports = easing; + + + + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + /** + * @module zrender/tool/color + */ + + + var kCSSColorTable = { + 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], + 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], + 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], + 'beige': [245,245,220,1], 'bisque': [255,228,196,1], + 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], + 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], + 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], + 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], + 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], + 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], + 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], + 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], + 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], + 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], + 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], + 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], + 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], + 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], + 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], + 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], + 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], + 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], + 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], + 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], + 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], + 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], + 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], + 'gray': [128,128,128,1], 'green': [0,128,0,1], + 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], + 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], + 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], + 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], + 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], + 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], + 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], + 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], + 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], + 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], + 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], + 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], + 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], + 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], + 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], + 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], + 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], + 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], + 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], + 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], + 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], + 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], + 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], + 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], + 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], + 'orange': [255,165,0,1], 'orangered': [255,69,0,1], + 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], + 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], + 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], + 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], + 'pink': [255,192,203,1], 'plum': [221,160,221,1], + 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], + 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], + 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], + 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], + 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], + 'sienna': [160,82,45,1], 'silver': [192,192,192,1], + 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], + 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], + 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], + 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], + 'teal': [0,128,128,1], 'thistle': [216,191,216,1], + 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], + 'violet': [238,130,238,1], 'wheat': [245,222,179,1], + 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], + 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] + }; + + function clampCssByte(i) { // Clamp to integer 0 .. 255. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 255 ? 255 : i; + } + + function clampCssAngle(i) { // Clamp to integer 0 .. 360. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 360 ? 360 : i; + } + + function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. + return f < 0 ? 0 : f > 1 ? 1 : f; + } + + function parseCssInt(str) { // int or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssByte(parseFloat(str) / 100 * 255); + } + return clampCssByte(parseInt(str, 10)); + } + + function parseCssFloat(str) { // float or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssFloat(parseFloat(str) / 100); + } + return clampCssFloat(parseFloat(str)); + } + + function cssHueToRgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + else if (h > 1) { + h -= 1; + } + + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2/3 - h) * 6; + } + return m1; + } + + function lerp(a, b, p) { + return a + (b - a) * p; + } + + /** + * @param {string} colorStr + * @return {Array.} + * @memberOf module:zrender/util/color + */ + function parse(colorStr) { + if (!colorStr) { + return; + } + // colorStr may be not string + colorStr = colorStr + ''; + // Remove all whitespace, not compliant, but should just be more accepting. + var str = colorStr.replace(/ /g, '').toLowerCase(); + + // Color keywords (and transparent) lookup. + if (str in kCSSColorTable) { + return kCSSColorTable[str].slice(); // dup. + } + + // #abc and #abc123 syntax. + if (str.charAt(0) === '#') { + if (str.length === 4) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xfff)) { + return; // Covers NaN. + } + return [ + ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), + (iv & 0xf0) | ((iv & 0xf0) >> 4), + (iv & 0xf) | ((iv & 0xf) << 4), + 1 + ]; + } + else if (str.length === 7) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xffffff)) { + return; // Covers NaN. + } + return [ + (iv & 0xff0000) >> 16, + (iv & 0xff00) >> 8, + iv & 0xff, + 1 + ]; + } + + return; + } + var op = str.indexOf('('), ep = str.indexOf(')'); + if (op !== -1 && ep + 1 === str.length) { + var fname = str.substr(0, op); + var params = str.substr(op + 1, ep - (op + 1)).split(','); + var alpha = 1; // To allow case fallthrough. + switch (fname) { + case 'rgba': + if (params.length !== 4) { + return; + } + alpha = parseCssFloat(params.pop()); // jshint ignore:line + // Fall through. + case 'rgb': + if (params.length !== 3) { + return; + } + return [ + parseCssInt(params[0]), + parseCssInt(params[1]), + parseCssInt(params[2]), + alpha + ]; + case 'hsla': + if (params.length !== 4) { + return; + } + params[3] = parseCssFloat(params[3]); + return hsla2rgba(params); + case 'hsl': + if (params.length !== 3) { + return; + } + return hsla2rgba(params); + default: + return; + } + } + + return; + } + + /** + * @param {Array.} hsla + * @return {Array.} rgba + */ + function hsla2rgba(hsla) { + var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 + // NOTE(deanm): According to the CSS spec s/l should only be + // percentages, but we don't bother and let float or percentage. + var s = parseCssFloat(hsla[1]); + var l = parseCssFloat(hsla[2]); + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + var rgba = [ + clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), + clampCssByte(cssHueToRgb(m1, m2, h) * 255), + clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255) + ]; + + if (hsla.length === 4) { + rgba[3] = hsla[3]; + } + + return rgba; + } + + /** + * @param {Array.} rgba + * @return {Array.} hsla + */ + function rgba2hsla(rgba) { + if (!rgba) { + return; + } + + // RGB from 0 to 255 + var R = rgba[0] / 255; + var G = rgba[1] / 255; + var B = rgba[2] / 255; + + var vMin = Math.min(R, G, B); // Min. value of RGB + var vMax = Math.max(R, G, B); // Max. value of RGB + var delta = vMax - vMin; // Delta RGB value + + var L = (vMax + vMin) / 2; + var H; + var S; + // HSL results from 0 to 1 + if (delta === 0) { + H = 0; + S = 0; + } + else { + if (L < 0.5) { + S = delta / (vMax + vMin); + } + else { + S = delta / (2 - vMax - vMin); + } + + var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; + var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; + var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + + if (R === vMax) { + H = deltaB - deltaG; + } + else if (G === vMax) { + H = (1 / 3) + deltaR - deltaB; + } + else if (B === vMax) { + H = (2 / 3) + deltaG - deltaR; + } + + if (H < 0) { + H += 1; + } + + if (H > 1) { + H -= 1; + } + } + + var hsla = [H * 360, S, L]; + + if (rgba[3] != null) { + hsla.push(rgba[3]); + } + + return hsla; + } + + /** + * @param {string} color + * @param {number} level + * @return {string} + * @memberOf module:zrender/util/color + */ + function lift(color, level) { + var colorArr = parse(color); + if (colorArr) { + for (var i = 0; i < 3; i++) { + if (level < 0) { + colorArr[i] = colorArr[i] * (1 - level) | 0; + } + else { + colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; + } + } + return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } + } + + /** + * @param {string} color + * @return {string} + * @memberOf module:zrender/util/color + */ + function toHex(color, level) { + var colorArr = parse(color); + if (colorArr) { + return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); + } + } + + /** + * Map value to color. Faster than mapToColor methods because color is represented by rgba array + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.>} colors List of rgba color array + * @param {Array.} [out] Mapped gba color array + * @return {Array.} + */ + function fastMapToColor(normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + out = out || [0, 0, 0, 0]; + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colors[leftIndex]; + var rightColor = colors[rightIndex]; + var dv = value - leftIndex; + out[0] = clampCssByte(lerp(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerp(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerp(leftColor[2], rightColor[2], dv)); + out[3] = clampCssByte(lerp(leftColor[3], rightColor[3], dv)); + return out; + } + /** + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.} colors Color list. + * @param {boolean=} fullOutput Default false. + * @return {(string|Object)} Result color. If fullOutput, + * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + * @memberOf module:zrender/util/color + */ + function mapToColor(normalizedValue, colors, fullOutput) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = parse(colors[leftIndex]); + var rightColor = parse(colors[rightIndex]); + var dv = value - leftIndex; + + var color = stringify( + [ + clampCssByte(lerp(leftColor[0], rightColor[0], dv)), + clampCssByte(lerp(leftColor[1], rightColor[1], dv)), + clampCssByte(lerp(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerp(leftColor[3], rightColor[3], dv)) + ], + 'rgba' + ); + + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; + } + + /** + * @param {string} color + * @param {number=} h 0 ~ 360, ignore when null. + * @param {number=} s 0 ~ 1, ignore when null. + * @param {number=} l 0 ~ 1, ignore when null. + * @return {string} Color string in rgba format. + * @memberOf module:zrender/util/color + */ + function modifyHSL(color, h, s, l) { + color = parse(color); + + if (color) { + color = rgba2hsla(color); + h != null && (color[0] = clampCssAngle(h)); + s != null && (color[1] = parseCssFloat(s)); + l != null && (color[2] = parseCssFloat(l)); + + return stringify(hsla2rgba(color), 'rgba'); + } + } + + /** + * @param {string} color + * @param {number=} alpha 0 ~ 1 + * @return {string} Color string in rgba format. + * @memberOf module:zrender/util/color + */ + function modifyAlpha(color, alpha) { + color = parse(color); + + if (color && alpha != null) { + color[3] = clampCssFloat(alpha); + return stringify(color, 'rgba'); + } + } + + /** + * @param {Array.} colors Color list. + * @param {string} type 'rgba', 'hsva', ... + * @return {string} Result color. + */ + function stringify(arrColor, type) { + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; + } + + module.exports = { + parse: parse, + lift: lift, + toHex: toHex, + fastMapToColor: fastMapToColor, + mapToColor: mapToColor, + modifyHSL: modifyHSL, + modifyAlpha: modifyAlpha, + stringify: stringify + }; + + + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + + var config = __webpack_require__(41); + + /** + * @exports zrender/tool/log + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + module.exports = function() { + if (config.debugMode === 0) { + return; + } + else if (config.debugMode == 1) { + for (var k in arguments) { + throw new Error(arguments[k]); + } + } + else if (config.debugMode > 1) { + for (var k in arguments) { + console.log(arguments[k]); + } + } + }; + + /* for debug + return function(mes) { + document.getElementById('wrong-message').innerHTML = + mes + ' ' + (new Date() - 0) + + '
' + + document.getElementById('wrong-message').innerHTML; + }; + */ + + + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + + var dpr = 1; + // If in browser environment + if (typeof window !== 'undefined') { + dpr = Math.max(window.devicePixelRatio || 1, 1); + } + /** + * config默认配置项 + * @exports zrender/config + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + var config = { + /** + * debug日志选项:catchBrushException为true下有效 + * 0 : 不生成debug数据,发布用 + * 1 : 异常抛出,调试用 + * 2 : 控制台输出,调试用 + */ + debugMode: 0, + + // retina 屏幕优化 + devicePixelRatio: dpr + }; + module.exports = config; + + + + +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Group = __webpack_require__(30); + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + + function Chart() { + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = componentUtil.getUID('viewChart'); + } + + Chart.prototype = { + + type: 'chart', + + /** + * Init the chart + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + init: function (ecModel, api) {}, + + /** + * Render the chart + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + render: function (seriesModel, ecModel, api, payload) {}, + + /** + * Highlight series or specified data item + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + highlight: function (seriesModel, ecModel, api, payload) { + toggleHighlight(seriesModel.getData(), payload, 'emphasis'); + }, + + /** + * Downplay series or specified data item + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + downplay: function (seriesModel, ecModel, api, payload) { + toggleHighlight(seriesModel.getData(), payload, 'normal'); + }, + + /** + * Remove self + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + remove: function (ecModel, api) { + this.group.removeAll(); + }, + + /** + * Dispose self + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + dispose: function () {} + }; + + var chartProto = Chart.prototype; + chartProto.updateView + = chartProto.updateLayout + = chartProto.updateVisual + = function (seriesModel, ecModel, api, payload) { + this.render(seriesModel, ecModel, api, payload); + }; + + /** + * Set state of single element + * @param {module:zrender/Element} el + * @param {string} state + */ + function elSetState(el, state) { + if (el) { + el.trigger(state); + if (el.type === 'group') { + for (var i = 0; i < el.childCount(); i++) { + elSetState(el.childAt(i), state); + } + } + } + } + /** + * @param {module:echarts/data/List} data + * @param {Object} payload + * @param {string} state 'normal'|'emphasis' + * @inner + */ + function toggleHighlight(data, payload, state) { + var dataIndex = payload && payload.dataIndex; + var name = payload && payload.name; + + if (dataIndex != null) { + var dataIndices = dataIndex instanceof Array ? dataIndex : [dataIndex]; + for (var i = 0, len = dataIndices.length; i < len; i++) { + elSetState(data.getItemGraphicEl(dataIndices[i]), state); + } + } + else if (name) { + var names = name instanceof Array ? name : [name]; + for (var i = 0, len = names.length; i < len; i++) { + var dataIndex = data.indexOfName(names[i]); + elSetState(data.getItemGraphicEl(dataIndex), state); + } + } + else { + data.eachItemGraphicEl(function (el) { + elSetState(el, state); + }); + } + } + + // Enable Chart.extend. + clazzUtil.enableClassExtend(Chart); + + // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement(Chart, {registerWhenExtend: true}); + + module.exports = Chart; + + +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var pathTool = __webpack_require__(44); + var round = Math.round; + var Path = __webpack_require__(45); + var colorTool = __webpack_require__(39); + var matrix = __webpack_require__(11); + var vector = __webpack_require__(10); + var Gradient = __webpack_require__(61); + + var graphic = {}; + + graphic.Group = __webpack_require__(30); + + graphic.Image = __webpack_require__(62); + + graphic.Text = __webpack_require__(64); + + graphic.Circle = __webpack_require__(65); + + graphic.Sector = __webpack_require__(66); + + graphic.Ring = __webpack_require__(67); + + graphic.Polygon = __webpack_require__(68); + + graphic.Polyline = __webpack_require__(72); + + graphic.Rect = __webpack_require__(73); + + graphic.Line = __webpack_require__(75); + + graphic.BezierCurve = __webpack_require__(76); + + graphic.Arc = __webpack_require__(77); + + graphic.CompoundPath = __webpack_require__(78); + + graphic.LinearGradient = __webpack_require__(79); + + graphic.RadialGradient = __webpack_require__(80); + + graphic.BoundingRect = __webpack_require__(9); + + /** + * Extend shape with parameters + */ + graphic.extendShape = function (opts) { + return Path.extend(opts); + }; + + /** + * Extend path + */ + graphic.extendPath = function (pathData, opts) { + return pathTool.extendFromString(pathData, opts); + }; + + /** + * Create a path element from path data string + * @param {string} pathData + * @param {Object} opts + * @param {module:zrender/core/BoundingRect} rect + * @param {string} [layout=cover] 'center' or 'cover' + */ + graphic.makePath = function (pathData, opts, rect, layout) { + var path = pathTool.createFromString(pathData, opts); + var boundingRect = path.getBoundingRect(); + if (rect) { + var aspect = boundingRect.width / boundingRect.height; + + if (layout === 'center') { + // Set rect to center, keep width / height ratio. + var width = rect.height * aspect; + var height; + if (width <= rect.width) { + height = rect.height; + } + else { + width = rect.width; + height = width / aspect; + } + var cx = rect.x + rect.width / 2; + var cy = rect.y + rect.height / 2; + + rect.x = cx - width / 2; + rect.y = cy - height / 2; + rect.width = width; + rect.height = height; + } + + this.resizePath(path, rect); + } + return path; + }; + + graphic.mergePath = pathTool.mergePath, + + /** + * Resize a path to fit the rect + * @param {module:zrender/graphic/Path} path + * @param {Object} rect + */ + graphic.resizePath = function (path, rect) { + if (!path.applyTransform) { + return; + } + + var pathRect = path.getBoundingRect(); + + var m = pathRect.calculateTransform(rect); + + path.applyTransform(m); + }; + + /** + * Sub pixel optimize line for canvas + * + * @param {Object} param + * @param {Object} [param.shape] + * @param {number} [param.shape.x1] + * @param {number} [param.shape.y1] + * @param {number} [param.shape.x2] + * @param {number} [param.shape.y2] + * @param {Object} [param.style] + * @param {number} [param.style.lineWidth] + * @return {Object} Modified param + */ + graphic.subPixelOptimizeLine = function (param) { + var subPixelOptimize = graphic.subPixelOptimize; + var shape = param.shape; + var lineWidth = param.style.lineWidth; + + if (round(shape.x1 * 2) === round(shape.x2 * 2)) { + shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true); + } + if (round(shape.y1 * 2) === round(shape.y2 * 2)) { + shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true); + } + return param; + }; + + /** + * Sub pixel optimize rect for canvas + * + * @param {Object} param + * @param {Object} [param.shape] + * @param {number} [param.shape.x] + * @param {number} [param.shape.y] + * @param {number} [param.shape.width] + * @param {number} [param.shape.height] + * @param {Object} [param.style] + * @param {number} [param.style.lineWidth] + * @return {Object} Modified param + */ + graphic.subPixelOptimizeRect = function (param) { + var subPixelOptimize = graphic.subPixelOptimize; + var shape = param.shape; + var lineWidth = param.style.lineWidth; + var originX = shape.x; + var originY = shape.y; + var originWidth = shape.width; + var originHeight = shape.height; + shape.x = subPixelOptimize(shape.x, lineWidth, true); + shape.y = subPixelOptimize(shape.y, lineWidth, true); + shape.width = Math.max( + subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x, + originWidth === 0 ? 0 : 1 + ); + shape.height = Math.max( + subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y, + originHeight === 0 ? 0 : 1 + ); + return param; + }; + + /** + * Sub pixel optimize for canvas + * + * @param {number} position Coordinate, such as x, y + * @param {number} lineWidth Should be nonnegative integer. + * @param {boolean=} positiveOrNegative Default false (negative). + * @return {number} Optimized position. + */ + graphic.subPixelOptimize = function (position, lineWidth, positiveOrNegative) { + // Assure that (position + lineWidth / 2) is near integer edge, + // otherwise line will be fuzzy in canvas. + var doubledPosition = round(position * 2); + return (doubledPosition + round(lineWidth)) % 2 === 0 + ? doubledPosition / 2 + : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; + }; + + function hasFillOrStroke(fillOrStroke) { + return fillOrStroke != null && fillOrStroke != 'none'; + } + + function liftColor(color) { + return typeof color === 'string' ? colorTool.lift(color, -0.1) : color; + } + + /** + * @private + */ + function cacheElementStl(el) { + if (el.__hoverStlDirty) { + var stroke = el.style.stroke; + var fill = el.style.fill; + + // Create hoverStyle on mouseover + var hoverStyle = el.__hoverStl; + hoverStyle.fill = hoverStyle.fill + || (hasFillOrStroke(fill) ? liftColor(fill) : null); + hoverStyle.stroke = hoverStyle.stroke + || (hasFillOrStroke(stroke) ? liftColor(stroke) : null); + + var normalStyle = {}; + for (var name in hoverStyle) { + if (hoverStyle.hasOwnProperty(name)) { + normalStyle[name] = el.style[name]; + } + } + + el.__normalStl = normalStyle; + + el.__hoverStlDirty = false; + } + } + + /** + * @private + */ + function doSingleEnterHover(el) { + if (el.__isHover) { + return; + } + + cacheElementStl(el); + + if (el.useHoverLayer) { + el.__zr && el.__zr.addHover(el, el.__hoverStl); + } + else { + el.setStyle(el.__hoverStl); + el.z2 += 1; + } + + el.__isHover = true; + } + + /** + * @inner + */ + function doSingleLeaveHover(el) { + if (!el.__isHover) { + return; + } + + var normalStl = el.__normalStl; + if (el.useHoverLayer) { + el.__zr && el.__zr.removeHover(el); + } + else { + normalStl && el.setStyle(normalStl); + el.z2 -= 1; + } + + el.__isHover = false; + } + + /** + * @inner + */ + function doEnterHover(el) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + doSingleEnterHover(child); + } + }) + : doSingleEnterHover(el); + } + + function doLeaveHover(el) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + doSingleLeaveHover(child); + } + }) + : doSingleLeaveHover(el); + } + + /** + * @inner + */ + function setElementHoverStl(el, hoverStl) { + // If element has sepcified hoverStyle, then use it instead of given hoverStyle + // Often used when item group has a label element and it's hoverStyle is different + el.__hoverStl = el.hoverStyle || hoverStl || {}; + el.__hoverStlDirty = true; + + if (el.__isHover) { + cacheElementStl(el); + } + } + + /** + * @inner + */ + function onElementMouseOver() { + // Only if element is not in emphasis status + !this.__isEmphasis && doEnterHover(this); + } + + /** + * @inner + */ + function onElementMouseOut() { + // Only if element is not in emphasis status + !this.__isEmphasis && doLeaveHover(this); + } + + /** + * @inner + */ + function enterEmphasis() { + this.__isEmphasis = true; + doEnterHover(this); + } + + /** + * @inner + */ + function leaveEmphasis() { + this.__isEmphasis = false; + doLeaveHover(this); + } + + /** + * Set hover style of element + * @param {module:zrender/Element} el + * @param {Object} [hoverStyle] + */ + graphic.setHoverStyle = function (el, hoverStyle) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + setElementHoverStl(child, hoverStyle); + } + }) + : setElementHoverStl(el, hoverStyle); + // Remove previous bound handlers + el.on('mouseover', onElementMouseOver) + .on('mouseout', onElementMouseOut); + + // Emphasis, normal can be triggered manually + el.on('emphasis', enterEmphasis) + .on('normal', leaveEmphasis); + }; + + /** + * Set text option in the style + * @param {Object} textStyle + * @param {module:echarts/model/Model} labelModel + * @param {string} color + */ + graphic.setText = function (textStyle, labelModel, color) { + var labelPosition = labelModel.getShallow('position') || 'inside'; + var labelColor = labelPosition.indexOf('inside') >= 0 ? 'white' : color; + var textStyleModel = labelModel.getModel('textStyle'); + zrUtil.extend(textStyle, { + textDistance: labelModel.getShallow('distance') || 5, + textFont: textStyleModel.getFont(), + textPosition: labelPosition, + textFill: textStyleModel.getTextColor() || labelColor + }); + }; + + function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) { + if (typeof dataIndex === 'function') { + cb = dataIndex; + dataIndex = null; + } + var animationEnabled = animatableModel + && ( + animatableModel.ifEnableAnimation + ? animatableModel.ifEnableAnimation() + // Directly use animation property + : animatableModel.getShallow('animation') + ); + + if (animationEnabled) { + var postfix = isUpdate ? 'Update' : ''; + var duration = animatableModel + && animatableModel.getShallow('animationDuration' + postfix); + var animationEasing = animatableModel + && animatableModel.getShallow('animationEasing' + postfix); + var animationDelay = animatableModel + && animatableModel.getShallow('animationDelay' + postfix); + if (typeof animationDelay === 'function') { + animationDelay = animationDelay(dataIndex); + } + duration > 0 + ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb) + : (el.attr(props), cb && cb()); + } + else { + el.attr(props); + cb && cb(); + } + } + /** + * Update graphic element properties with or without animation according to the configuration in series + * @param {module:zrender/Element} el + * @param {Object} props + * @param {module:echarts/model/Model} [animatableModel] + * @param {number} [dataIndex] + * @param {Function} [cb] + * @example + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); + * // Or + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, function () { console.log('Animation done!'); }); + */ + graphic.updateProps = function (el, props, animatableModel, dataIndex, cb) { + animateOrSetProps(true, el, props, animatableModel, dataIndex, cb); + }; + + /** + * Init graphic element properties with or without animation according to the configuration in series + * @param {module:zrender/Element} el + * @param {Object} props + * @param {module:echarts/model/Model} [animatableModel] + * @param {number} [dataIndex] + * @param {Function} cb + */ + graphic.initProps = function (el, props, animatableModel, dataIndex, cb) { + animateOrSetProps(false, el, props, animatableModel, dataIndex, cb); + }; + + /** + * Get transform matrix of target (param target), + * in coordinate of its ancestor (param ancestor) + * + * @param {module:zrender/mixin/Transformable} target + * @param {module:zrender/mixin/Transformable} [ancestor] + */ + graphic.getTransform = function (target, ancestor) { + var mat = matrix.identity([]); + + while (target && target !== ancestor) { + matrix.mul(mat, target.getLocalTransform(), mat); + target = target.parent; + } + + return mat; + }; + + /** + * Apply transform to an vertex. + * @param {Array.} vertex [x, y] + * @param {Array.} transform Transform matrix: like [1, 0, 0, 1, 0, 0] + * @param {boolean=} invert Whether use invert matrix. + * @return {Array.} [x, y] + */ + graphic.applyTransform = function (vertex, transform, invert) { + if (invert) { + transform = matrix.invert([], transform); + } + return vector.applyTransform([], vertex, transform); + }; + + /** + * @param {string} direction 'left' 'right' 'top' 'bottom' + * @param {Array.} transform Transform matrix: like [1, 0, 0, 1, 0, 0] + * @param {boolean=} invert Whether use invert matrix. + * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom' + */ + graphic.transformDirection = function (direction, transform, invert) { + + // Pick a base, ensure that transform result will not be (0, 0). + var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0) + ? 1 : Math.abs(2 * transform[4] / transform[0]); + var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0) + ? 1 : Math.abs(2 * transform[4] / transform[2]); + + var vertex = [ + direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, + direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0 + ]; + + vertex = graphic.applyTransform(vertex, transform, invert); + + return Math.abs(vertex[0]) > Math.abs(vertex[1]) + ? (vertex[0] > 0 ? 'right' : 'left') + : (vertex[1] > 0 ? 'bottom' : 'top'); + }; + + /** + * Apply group transition animation from g1 to g2 + */ + graphic.groupTransition = function (g1, g2, animatableModel, cb) { + if (!g1 || !g2) { + return; + } + + function getElMap(g) { + var elMap = {}; + g.traverse(function (el) { + if (!el.isGroup && el.anid) { + elMap[el.anid] = el; + } + }); + return elMap; + } + function getAnimatableProps(el) { + var obj = { + position: vector.clone(el.position), + rotation: el.rotation + }; + if (el.shape) { + obj.shape = zrUtil.extend({}, el.shape); + } + return obj; + } + var elMap1 = getElMap(g1); + + g2.traverse(function (el) { + if (!el.isGroup && el.anid) { + var oldEl = elMap1[el.anid]; + if (oldEl) { + var newProp = getAnimatableProps(el); + el.attr(getAnimatableProps(oldEl)); + graphic.updateProps(el, newProp, animatableModel, el.dataIndex); + } + // else { + // if (el.previousProps) { + // graphic.updateProps + // } + // } + } + }); + }; + + module.exports = graphic; + + +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Path = __webpack_require__(45); + var PathProxy = __webpack_require__(49); + var transformPath = __webpack_require__(60); + var matrix = __webpack_require__(11); + + // command chars + var cc = [ + 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', + 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' + ]; + + var mathSqrt = Math.sqrt; + var mathSin = Math.sin; + var mathCos = Math.cos; + var PI = Math.PI; + + var vMag = function(v) { + return Math.sqrt(v[0] * v[0] + v[1] * v[1]); + }; + var vRatio = function(u, v) { + return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); + }; + var vAngle = function(u, v) { + return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) + * Math.acos(vRatio(u, v)); + }; + + function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) { + var psi = psiDeg * (PI / 180.0); + var xp = mathCos(psi) * (x1 - x2) / 2.0 + + mathSin(psi) * (y1 - y2) / 2.0; + var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0 + + mathCos(psi) * (y1 - y2) / 2.0; + + var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry); + + if (lambda > 1) { + rx *= mathSqrt(lambda); + ry *= mathSqrt(lambda); + } + + var f = (fa === fs ? -1 : 1) + * mathSqrt((((rx * rx) * (ry * ry)) + - ((rx * rx) * (yp * yp)) + - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + + (ry * ry) * (xp * xp)) + ) || 0; + + var cxp = f * rx * yp / ry; + var cyp = f * -ry * xp / rx; + + var cx = (x1 + x2) / 2.0 + + mathCos(psi) * cxp + - mathSin(psi) * cyp; + var cy = (y1 + y2) / 2.0 + + mathSin(psi) * cxp + + mathCos(psi) * cyp; + + var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]); + var u = [ (xp - cxp) / rx, (yp - cyp) / ry ]; + var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ]; + var dTheta = vAngle(u, v); + + if (vRatio(u, v) <= -1) { + dTheta = PI; + } + if (vRatio(u, v) >= 1) { + dTheta = 0; + } + if (fs === 0 && dTheta > 0) { + dTheta = dTheta - 2 * PI; + } + if (fs === 1 && dTheta < 0) { + dTheta = dTheta + 2 * PI; + } + + path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs); + } + + function createPathProxyFromString(data) { + if (!data) { + return []; + } + + // command string + var cs = data.replace(/-/g, ' -') + .replace(/ /g, ' ') + .replace(/ /g, ',') + .replace(/,,/g, ','); + + var n; + // create pipes so that we can split the data + for (n = 0; n < cc.length; n++) { + cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); + } + + // create array + var arr = cs.split('|'); + // init context point + var cpx = 0; + var cpy = 0; + + var path = new PathProxy(); + var CMD = PathProxy.CMD; + + var prevCmd; + for (n = 1; n < arr.length; n++) { + var str = arr[n]; + var c = str.charAt(0); + var off = 0; + var p = str.slice(1).replace(/e,-/g, 'e-').split(','); + var cmd; + + if (p.length > 0 && p[0] === '') { + p.shift(); + } + + for (var i = 0; i < p.length; i++) { + p[i] = parseFloat(p[i]); + } + while (off < p.length && !isNaN(p[off])) { + if (isNaN(p[0])) { + break; + } + var ctlPtx; + var ctlPty; + + var rx; + var ry; + var psi; + var fa; + var fs; + + var x1 = cpx; + var y1 = cpy; + + // convert l, H, h, V, and v to L + switch (c) { + case 'l': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'L': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'm': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + c = 'l'; + break; + case 'M': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + c = 'L'; + break; + case 'h': + cpx += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'H': + cpx = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'v': + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'V': + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'C': + cmd = CMD.C; + path.addData( + cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++] + ); + cpx = p[off - 2]; + cpy = p[off - 1]; + break; + case 'c': + cmd = CMD.C; + path.addData( + cmd, + p[off++] + cpx, p[off++] + cpy, + p[off++] + cpx, p[off++] + cpy, + p[off++] + cpx, p[off++] + cpy + ); + cpx += p[off - 2]; + cpy += p[off - 1]; + break; + case 'S': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 's': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = cpx + p[off++]; + y1 = cpy + p[off++]; + cpx += p[off++]; + cpy += p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 'Q': + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'q': + x1 = p[off++] + cpx; + y1 = p[off++] + cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'T': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 't': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 'A': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + + x1 = cpx, y1 = cpy; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.A; + processArc( + x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path + ); + break; + case 'a': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + + x1 = cpx, y1 = cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.A; + processArc( + x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path + ); + break; + } + } + + if (c === 'z' || c === 'Z') { + cmd = CMD.Z; + path.addData(cmd); + } + + prevCmd = cmd; + } + + path.toStatic(); + + return path; + } + + // TODO Optimize double memory cost problem + function createPathOptions(str, opts) { + var pathProxy = createPathProxyFromString(str); + var transform; + opts = opts || {}; + opts.buildPath = function (path) { + path.setData(pathProxy.data); + transform && transformPath(path, transform); + // Svg and vml renderer don't have context + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx); + } + }; + + opts.applyTransform = function (m) { + if (!transform) { + transform = matrix.create(); + } + matrix.mul(transform, m, transform); + this.dirty(true); + }; + + return opts; + } + + module.exports = { + /** + * Create a Path object from path string data + * http://www.w3.org/TR/SVG/paths.html#PathData + * @param {Object} opts Other options + */ + createFromString: function (str, opts) { + return new Path(createPathOptions(str, opts)); + }, + + /** + * Create a Path class from path string data + * @param {string} str + * @param {Object} opts Other options + */ + extendFromString: function (str, opts) { + return Path.extend(createPathOptions(str, opts)); + }, + + /** + * Merge multiple paths + */ + // TODO Apply transform + // TODO stroke dash + // TODO Optimize double memory cost problem + mergePath: function (pathEls, opts) { + var pathList = []; + var len = pathEls.length; + for (var i = 0; i < len; i++) { + var pathEl = pathEls[i]; + if (pathEl.__dirty) { + pathEl.buildPath(pathEl.path, pathEl.shape, true); + } + pathList.push(pathEl.path); + } + + var pathBundle = new Path(opts); + pathBundle.buildPath = function (path) { + path.appendPath(pathList); + // Svg and vml renderer don't have context + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx); + } + }; + + return pathBundle; + } + }; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Path element + * @module zrender/graphic/Path + */ + + + + var Displayable = __webpack_require__(46); + var zrUtil = __webpack_require__(4); + var PathProxy = __webpack_require__(49); + var pathContain = __webpack_require__(52); + + var Pattern = __webpack_require__(59); + var getCanvasPattern = Pattern.prototype.getCanvasPattern; + + var abs = Math.abs; + + /** + * @alias module:zrender/graphic/Path + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + function Path(opts) { + Displayable.call(this, opts); + + /** + * @type {module:zrender/core/PathProxy} + * @readOnly + */ + this.path = new PathProxy(); + } + + Path.prototype = { + + constructor: Path, + + type: 'path', + + __dirtyPath: true, + + strokeContainThreshold: 5, + + brush: function (ctx, prevEl) { + var style = this.style; + var path = this.path; + var hasStroke = style.hasStroke(); + var hasFill = style.hasFill(); + var fill = style.fill; + var stroke = style.stroke; + var hasFillGradient = hasFill && !!(fill.colorStops); + var hasStrokeGradient = hasStroke && !!(stroke.colorStops); + var hasFillPattern = hasFill && !!(fill.image); + var hasStrokePattern = hasStroke && !!(stroke.image); + + style.bind(ctx, this, prevEl); + this.setTransform(ctx); + + if (this.__dirty) { + var rect = this.getBoundingRect(); + // Update gradient because bounding rect may changed + if (hasFillGradient) { + this._fillGradient = style.getGradient(ctx, fill, rect); + } + if (hasStrokeGradient) { + this._strokeGradient = style.getGradient(ctx, stroke, rect); + } + } + // Use the gradient or pattern + if (hasFillGradient) { + // PENDING If may have affect the state + ctx.fillStyle = this._fillGradient; + } + else if (hasFillPattern) { + ctx.fillStyle = getCanvasPattern.call(fill, ctx); + } + if (hasStrokeGradient) { + ctx.strokeStyle = this._strokeGradient; + } + else if (hasStrokePattern) { + ctx.strokeStyle = getCanvasPattern.call(stroke, ctx); + } + + var lineDash = style.lineDash; + var lineDashOffset = style.lineDashOffset; + + var ctxLineDash = !!ctx.setLineDash; + + // Update path sx, sy + var scale = this.getGlobalScale(); + path.setScale(scale[0], scale[1]); + + // Proxy context + // Rebuild path in following 2 cases + // 1. Path is dirty + // 2. Path needs javascript implemented lineDash stroking. + // In this case, lineDash information will not be saved in PathProxy + if (this.__dirtyPath || ( + lineDash && !ctxLineDash && hasStroke + )) { + path = this.path.beginPath(ctx); + + // Setting line dash before build path + if (lineDash && !ctxLineDash) { + path.setLineDash(lineDash); + path.setLineDashOffset(lineDashOffset); + } + + this.buildPath(path, this.shape, false); + + // Clear path dirty flag + this.__dirtyPath = false; + } + else { + // Replay path building + ctx.beginPath(); + this.path.rebuildPath(ctx); + } + + hasFill && path.fill(ctx); + + if (lineDash && ctxLineDash) { + ctx.setLineDash(lineDash); + ctx.lineDashOffset = lineDashOffset; + } + + hasStroke && path.stroke(ctx); + + if (lineDash && ctxLineDash) { + // PENDING + // Remove lineDash + ctx.setLineDash([]); + } + + + this.restoreTransform(ctx); + + // Draw rect text + if (style.text || style.text === 0) { + // var rect = this.getBoundingRect(); + this.drawRectText(ctx, this.getBoundingRect()); + } + }, + + // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath + // Like in circle + buildPath: function (ctx, shapeCfg, inBundle) {}, + + getBoundingRect: function () { + var rect = this._rect; + var style = this.style; + var needsUpdateRect = !rect; + if (needsUpdateRect) { + var path = this.path; + if (this.__dirtyPath) { + path.beginPath(); + this.buildPath(path, this.shape, false); + } + rect = path.getBoundingRect(); + } + this._rect = rect; + + if (style.hasStroke()) { + // Needs update rect with stroke lineWidth when + // 1. Element changes scale or lineWidth + // 2. Shape is changed + var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone()); + if (this.__dirty || needsUpdateRect) { + rectWithStroke.copy(rect); + // FIXME Must after updateTransform + var w = style.lineWidth; + // PENDING, Min line width is needed when line is horizontal or vertical + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + + // Only add extra hover lineWidth when there are no fill + if (!style.hasFill()) { + w = Math.max(w, this.strokeContainThreshold || 4); + } + // Consider line width + // Line scale can't be 0; + if (lineScale > 1e-10) { + rectWithStroke.width += w / lineScale; + rectWithStroke.height += w / lineScale; + rectWithStroke.x -= w / lineScale / 2; + rectWithStroke.y -= w / lineScale / 2; + } + } + + // Return rect with stroke + return rectWithStroke; + } + + return rect; + }, + + contain: function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + var style = this.style; + x = localPos[0]; + y = localPos[1]; + + if (rect.contain(x, y)) { + var pathData = this.path.data; + if (style.hasStroke()) { + var lineWidth = style.lineWidth; + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + // Line scale can't be 0; + if (lineScale > 1e-10) { + // Only add extra hover lineWidth when there are no fill + if (!style.hasFill()) { + lineWidth = Math.max(lineWidth, this.strokeContainThreshold); + } + if (pathContain.containStroke( + pathData, lineWidth / lineScale, x, y + )) { + return true; + } + } + } + if (style.hasFill()) { + return pathContain.contain(pathData, x, y); + } + } + return false; + }, + + /** + * @param {boolean} dirtyPath + */ + dirty: function (dirtyPath) { + if (dirtyPath == null) { + dirtyPath = true; + } + // Only mark dirty, not mark clean + if (dirtyPath) { + this.__dirtyPath = dirtyPath; + this._rect = null; + } + + this.__dirty = true; + + this.__zr && this.__zr.refresh(); + + // Used as a clipping path + if (this.__clipTarget) { + this.__clipTarget.dirty(); + } + }, + + /** + * Alias for animate('shape') + * @param {boolean} loop + */ + animateShape: function (loop) { + return this.animate('shape', loop); + }, + + // Overwrite attrKV + attrKV: function (key, value) { + // FIXME + if (key === 'shape') { + this.setShape(value); + this.__dirtyPath = true; + this._rect = null; + } + else { + Displayable.prototype.attrKV.call(this, key, value); + } + }, + + /** + * @param {Object|string} key + * @param {*} value + */ + setShape: function (key, value) { + var shape = this.shape; + // Path from string may not have shape + if (shape) { + if (zrUtil.isObject(key)) { + for (var name in key) { + shape[name] = key[name]; + } + } + else { + shape[key] = value; + } + this.dirty(true); + } + return this; + }, + + getLineScale: function () { + var m = this.transform; + // Get the line scale. + // Determinant of `m` means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 + ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) + : 1; + } + }; + + /** + * 扩展一个 Path element, 比如星形,圆等。 + * Extend a path element + * @param {Object} props + * @param {string} props.type Path type + * @param {Function} props.init Initialize + * @param {Function} props.buildPath Overwrite buildPath method + * @param {Object} [props.style] Extended default style config + * @param {Object} [props.shape] Extended default shape config + */ + Path.extend = function (defaults) { + var Sub = function (opts) { + Path.call(this, opts); + + if (defaults.style) { + // Extend default style + this.style.extendFrom(defaults.style, false); + } + + // Extend default shape + var defaultShape = defaults.shape; + if (defaultShape) { + this.shape = this.shape || {}; + var thisShape = this.shape; + for (var name in defaultShape) { + if ( + ! thisShape.hasOwnProperty(name) + && defaultShape.hasOwnProperty(name) + ) { + thisShape[name] = defaultShape[name]; + } + } + } + + defaults.init && defaults.init.call(this, opts); + }; + + zrUtil.inherits(Sub, Path); + + // FIXME 不能 extend position, rotation 等引用对象 + for (var name in defaults) { + // Extending prototype values and methods + if (name !== 'style' && name !== 'shape') { + Sub.prototype[name] = defaults[name]; + } + } + + return Sub; + }; + + zrUtil.inherits(Path, Displayable); + + module.exports = Path; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 可绘制的图形基类 + * Base class of all displayable graphic objects + * @module zrender/graphic/Displayable + */ + + + + var zrUtil = __webpack_require__(4); + + var Style = __webpack_require__(47); + + var Element = __webpack_require__(31); + var RectText = __webpack_require__(48); + // var Stateful = require('./mixin/Stateful'); + + /** + * @alias module:zrender/graphic/Displayable + * @extends module:zrender/Element + * @extends module:zrender/graphic/mixin/RectText + */ + function Displayable(opts) { + + opts = opts || {}; + + Element.call(this, opts); + + // Extend properties + for (var name in opts) { + if ( + opts.hasOwnProperty(name) && + name !== 'style' + ) { + this[name] = opts[name]; + } + } + + /** + * @type {module:zrender/graphic/Style} + */ + this.style = new Style(opts.style); + + this._rect = null; + // Shapes for cascade clipping. + this.__clipPaths = []; + + // FIXME Stateful must be mixined after style is setted + // Stateful.call(this, opts); + } + + Displayable.prototype = { + + constructor: Displayable, + + type: 'displayable', + + /** + * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制 + * Dirty flag. From which painter will determine if this displayable object needs brush + * @name module:zrender/graphic/Displayable#__dirty + * @type {boolean} + */ + __dirty: true, + + /** + * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件 + * If ignore drawing of the displayable object. Mouse event will still be triggered + * @name module:/zrender/graphic/Displayable#invisible + * @type {boolean} + * @default false + */ + invisible: false, + + /** + * @name module:/zrender/graphic/Displayable#z + * @type {number} + * @default 0 + */ + z: 0, + + /** + * @name module:/zrender/graphic/Displayable#z + * @type {number} + * @default 0 + */ + z2: 0, + + /** + * z层level,决定绘画在哪层canvas中 + * @name module:/zrender/graphic/Displayable#zlevel + * @type {number} + * @default 0 + */ + zlevel: 0, + + /** + * 是否可拖拽 + * @name module:/zrender/graphic/Displayable#draggable + * @type {boolean} + * @default false + */ + draggable: false, + + /** + * 是否正在拖拽 + * @name module:/zrender/graphic/Displayable#draggable + * @type {boolean} + * @default false + */ + dragging: false, + + /** + * 是否相应鼠标事件 + * @name module:/zrender/graphic/Displayable#silent + * @type {boolean} + * @default false + */ + silent: false, + + /** + * If enable culling + * @type {boolean} + * @default false + */ + culling: false, + + /** + * Mouse cursor when hovered + * @name module:/zrender/graphic/Displayable#cursor + * @type {string} + */ + cursor: 'pointer', + + /** + * If hover area is bounding rect + * @name module:/zrender/graphic/Displayable#rectHover + * @type {string} + */ + rectHover: false, + + /** + * Render the element progressively when the value >= 0, + * usefull for large data. + * @type {number} + */ + progressive: -1, + + beforeBrush: function (ctx) {}, + + afterBrush: function (ctx) {}, + + /** + * 图形绘制方法 + * @param {Canvas2DRenderingContext} ctx + */ + // Interface + brush: function (ctx, prevEl) {}, + + /** + * 获取最小包围盒 + * @return {module:zrender/core/BoundingRect} + */ + // Interface + getBoundingRect: function () {}, + + /** + * 判断坐标 x, y 是否在图形上 + * If displayable element contain coord x, y + * @param {number} x + * @param {number} y + * @return {boolean} + */ + contain: function (x, y) { + return this.rectContain(x, y); + }, + + /** + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) { + cb.call(context, this); + }, + + /** + * 判断坐标 x, y 是否在图形的包围盒上 + * If bounding rect of element contain coord x, y + * @param {number} x + * @param {number} y + * @return {boolean} + */ + rectContain: function (x, y) { + var coord = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + return rect.contain(coord[0], coord[1]); + }, + + /** + * 标记图形元素为脏,并且在下一帧重绘 + * Mark displayable element dirty and refresh next frame + */ + dirty: function () { + this.__dirty = true; + + this._rect = null; + + this.__zr && this.__zr.refresh(); + }, + + /** + * 图形是否会触发事件 + * If displayable object binded any event + * @return {boolean} + */ + // TODO, 通过 bind 绑定的事件 + // isSilent: function () { + // return !( + // this.hoverable || this.draggable + // || this.onmousemove || this.onmouseover || this.onmouseout + // || this.onmousedown || this.onmouseup || this.onclick + // || this.ondragenter || this.ondragover || this.ondragleave + // || this.ondrop + // ); + // }, + /** + * Alias for animate('style') + * @param {boolean} loop + */ + animateStyle: function (loop) { + return this.animate('style', loop); + }, + + attrKV: function (key, value) { + if (key !== 'style') { + Element.prototype.attrKV.call(this, key, value); + } + else { + this.style.set(value); + } + }, + + /** + * @param {Object|string} key + * @param {*} value + */ + setStyle: function (key, value) { + this.style.set(key, value); + this.dirty(false); + return this; + }, + + /** + * Use given style object + * @param {Object} obj + */ + useStyle: function (obj) { + this.style = new Style(obj); + this.dirty(false); + return this; + } + }; + + zrUtil.inherits(Displayable, Element); + + zrUtil.mixin(Displayable, RectText); + // zrUtil.mixin(Displayable, Stateful); + + module.exports = Displayable; + + +/***/ }, +/* 47 */ +/***/ function(module, exports) { + + /** + * @module zrender/graphic/Style + */ + + + var STYLE_COMMON_PROPS = [ + ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'], + ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10] + ]; + + // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4); + // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4); + + var Style = function (opts) { + this.extendFrom(opts); + }; + + function createLinearGradient(ctx, obj, rect) { + // var size = + var x = obj.x; + var x2 = obj.x2; + var y = obj.y; + var y2 = obj.y2; + + if (!obj.global) { + x = x * rect.width + rect.x; + x2 = x2 * rect.width + rect.x; + y = y * rect.height + rect.y; + y2 = y2 * rect.height + rect.y; + } + + var canvasGradient = ctx.createLinearGradient(x, y, x2, y2); + + return canvasGradient; + } + + function createRadialGradient(ctx, obj, rect) { + var width = rect.width; + var height = rect.height; + var min = Math.min(width, height); + + var x = obj.x; + var y = obj.y; + var r = obj.r; + if (!obj.global) { + x = x * width + rect.x; + y = y * height + rect.y; + r = r * min; + } + + var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); + + return canvasGradient; + } + + + Style.prototype = { + + constructor: Style, + + /** + * @type {string} + */ + fill: '#000000', + + /** + * @type {string} + */ + stroke: null, + + /** + * @type {number} + */ + opacity: 1, + + /** + * @type {Array.} + */ + lineDash: null, + + /** + * @type {number} + */ + lineDashOffset: 0, + + /** + * @type {number} + */ + shadowBlur: 0, + + /** + * @type {number} + */ + shadowOffsetX: 0, + + /** + * @type {number} + */ + shadowOffsetY: 0, + + /** + * @type {number} + */ + lineWidth: 1, + + /** + * If stroke ignore scale + * @type {Boolean} + */ + strokeNoScale: false, + + // Bounding rect text configuration + // Not affected by element transform + /** + * @type {string} + */ + text: null, + + /** + * @type {string} + */ + textFill: '#000', + + /** + * @type {string} + */ + textStroke: null, + + /** + * 'inside', 'left', 'right', 'top', 'bottom' + * [x, y] + * @type {string|Array.} + * @default 'inside' + */ + textPosition: 'inside', + + /** + * @type {string} + */ + textBaseline: null, + + /** + * @type {string} + */ + textAlign: null, + + /** + * @type {string} + */ + textVerticalAlign: null, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textDistance: 5, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowBlur: 0, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowOffsetX: 0, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowOffsetY: 0, + + /** + * If transform text + * Only useful in Path and Image element + * @type {boolean} + */ + textTransform: false, + + /** + * Text rotate around position of Path or Image + * Only useful in Path and Image element and textTransform is false. + */ + textRotation: 0, + + /** + * @type {string} + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + */ + blend: null, + + /** + * @param {CanvasRenderingContext2D} ctx + */ + bind: function (ctx, el, prevEl) { + var style = this; + var prevStyle = prevEl && prevEl.style; + var firstDraw = !prevStyle; + + for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { + var prop = STYLE_COMMON_PROPS[i]; + var styleName = prop[0]; + + if (firstDraw || style[styleName] !== prevStyle[styleName]) { + // FIXME Invalid property value will cause style leak from previous element. + ctx[styleName] = style[styleName] || prop[1]; + } + } + + if ((firstDraw || style.fill !== prevStyle.fill)) { + ctx.fillStyle = style.fill; + } + if ((firstDraw || style.stroke !== prevStyle.stroke)) { + ctx.strokeStyle = style.stroke; + } + if ((firstDraw || style.opacity !== prevStyle.opacity)) { + ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; + } + + if ((firstDraw || style.blend !== prevStyle.blend)) { + ctx.globalCompositeOperation = style.blend || 'source-over'; + } + if (this.hasStroke()) { + var lineWidth = style.lineWidth; + ctx.lineWidth = lineWidth / ( + (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1 + ); + } + }, + + hasFill: function () { + var fill = this.fill; + return fill != null && fill !== 'none'; + }, + + hasStroke: function () { + var stroke = this.stroke; + return stroke != null && stroke !== 'none' && this.lineWidth > 0; + }, + + /** + * Extend from other style + * @param {zrender/graphic/Style} otherStyle + * @param {boolean} overwrite + */ + extendFrom: function (otherStyle, overwrite) { + if (otherStyle) { + var target = this; + for (var name in otherStyle) { + if (otherStyle.hasOwnProperty(name) + && (overwrite || ! target.hasOwnProperty(name)) + ) { + target[name] = otherStyle[name]; + } + } + } + }, + + /** + * Batch setting style with a given object + * @param {Object|string} obj + * @param {*} [obj] + */ + set: function (obj, value) { + if (typeof obj === 'string') { + this[obj] = value; + } + else { + this.extendFrom(obj, true); + } + }, + + /** + * Clone + * @return {zrender/graphic/Style} [description] + */ + clone: function () { + var newStyle = new this.constructor(); + newStyle.extendFrom(this, true); + return newStyle; + }, + + getGradient: function (ctx, obj, rect) { + var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient; + var canvasGradient = method(ctx, obj, rect); + var colorStops = obj.colorStops; + for (var i = 0; i < colorStops.length; i++) { + canvasGradient.addColorStop( + colorStops[i].offset, colorStops[i].color + ); + } + return canvasGradient; + } + }; + + var styleProto = Style.prototype; + for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { + var prop = STYLE_COMMON_PROPS[i]; + if (!(prop[0] in styleProto)) { + styleProto[prop[0]] = prop[1]; + } + } + + // Provide for others + Style.getGradient = styleProto.getGradient; + + module.exports = Style; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Mixin for drawing text in a element bounding rect + * @module zrender/mixin/RectText + */ + + + + var textContain = __webpack_require__(8); + var BoundingRect = __webpack_require__(9); + + var tmpRect = new BoundingRect(); + + var RectText = function () {}; + + function parsePercent(value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; + } + + RectText.prototype = { + + constructor: RectText, + + /** + * Draw text in a rect with specified position. + * @param {CanvasRenderingContext} ctx + * @param {Object} rect Displayable rect + * @return {Object} textRect Alternative precalculated text bounding rect + */ + drawRectText: function (ctx, rect, textRect) { + var style = this.style; + var text = style.text; + // Convert to string + text != null && (text += ''); + if (!text) { + return; + } + + // FIXME + ctx.save(); + + var x; + var y; + var textPosition = style.textPosition; + var distance = style.textDistance; + var align = style.textAlign; + var font = style.textFont || style.font; + var baseline = style.textBaseline; + var verticalAlign = style.textVerticalAlign; + + textRect = textRect || textContain.getBoundingRect(text, font, align, baseline); + + // Transform rect to view space + var transform = this.transform; + if (!style.textTransform) { + if (transform) { + tmpRect.copy(rect); + tmpRect.applyTransform(transform); + rect = tmpRect; + } + } + else { + this.setTransform(ctx); + } + + // Text position represented by coord + if (textPosition instanceof Array) { + // Percent + x = rect.x + parsePercent(textPosition[0], rect.width); + y = rect.y + parsePercent(textPosition[1], rect.height); + align = align || 'left'; + baseline = baseline || 'top'; + + if (verticalAlign) { + switch (verticalAlign) { + case 'middle': + y -= textRect.height / 2 - textRect.lineHeight / 2; + break; + case 'bottom': + y -= textRect.height - textRect.lineHeight / 2; + break; + default: + y += textRect.lineHeight / 2; + } + // Force bseline to be middle + baseline = 'middle'; + } + } + else { + var res = textContain.adjustTextPositionOnRect( + textPosition, rect, textRect, distance + ); + x = res.x; + y = res.y; + // Default align and baseline when has textPosition + align = align || res.textAlign; + baseline = baseline || res.textBaseline; + } + + // Use canvas default left textAlign. Giving invalid value will cause state not change + ctx.textAlign = align || 'left'; + // Use canvas default alphabetic baseline + ctx.textBaseline = baseline || 'alphabetic'; + + var textFill = style.textFill; + var textStroke = style.textStroke; + textFill && (ctx.fillStyle = textFill); + textStroke && (ctx.strokeStyle = textStroke); + + // TODO Invalid font + ctx.font = font || '12px sans-serif'; + + // Text shadow + // Always set shadowBlur and shadowOffset to avoid leak from displayable + ctx.shadowBlur = style.textShadowBlur; + ctx.shadowColor = style.textShadowColor || 'transparent'; + ctx.shadowOffsetX = style.textShadowOffsetX; + ctx.shadowOffsetY = style.textShadowOffsetY; + + var textLines = text.split('\n'); + + if (style.textRotation) { + transform && ctx.translate(transform[4], transform[5]); + ctx.rotate(style.textRotation); + transform && ctx.translate(-transform[4], -transform[5]); + } + + for (var i = 0; i < textLines.length; i++) { + textFill && ctx.fillText(textLines[i], x, y); + textStroke && ctx.strokeText(textLines[i], x, y); + y += textRect.lineHeight; + } + + ctx.restore(); + } + }; + + module.exports = RectText; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中 + * 可以用于 isInsidePath 判断以及获取boundingRect + * + * @module zrender/core/PathProxy + * @author Yi Shen (http://www.github.com/pissang) + */ + + // TODO getTotalLength, getPointAtLength + + + var curve = __webpack_require__(50); + var vec2 = __webpack_require__(10); + var bbox = __webpack_require__(51); + var BoundingRect = __webpack_require__(9); + var dpr = __webpack_require__(41).devicePixelRatio; + + var CMD = { + M: 1, + L: 2, + C: 3, + Q: 4, + A: 5, + Z: 6, + // Rect + R: 7 + }; + + var min = []; + var max = []; + var min2 = []; + var max2 = []; + var mathMin = Math.min; + var mathMax = Math.max; + var mathCos = Math.cos; + var mathSin = Math.sin; + var mathSqrt = Math.sqrt; + var mathAbs = Math.abs; + + var hasTypedArray = typeof Float32Array != 'undefined'; + + /** + * @alias module:zrender/core/PathProxy + * @constructor + */ + var PathProxy = function () { + + /** + * Path data. Stored as flat array + * @type {Array.} + */ + this.data = []; + + this._len = 0; + + this._ctx = null; + + this._xi = 0; + this._yi = 0; + + this._x0 = 0; + this._y0 = 0; + + // Unit x, Unit y. Provide for avoiding drawing that too short line segment + this._ux = 0; + this._uy = 0; + }; + + /** + * 快速计算Path包围盒(并不是最小包围盒) + * @return {Object} + */ + PathProxy.prototype = { + + constructor: PathProxy, + + _lineDash: null, + + _dashOffset: 0, + + _dashIdx: 0, + + _dashSum: 0, + + /** + * @readOnly + */ + setScale: function (sx, sy) { + this._ux = mathAbs(1 / dpr / sx) || 0; + this._uy = mathAbs(1 / dpr / sy) || 0; + }, + + getContext: function () { + return this._ctx; + }, + + /** + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + beginPath: function (ctx) { + + this._ctx = ctx; + + ctx && ctx.beginPath(); + + ctx && (this.dpr = ctx.dpr); + + // Reset + this._len = 0; + + if (this._lineDash) { + this._lineDash = null; + + this._dashOffset = 0; + } + + return this; + }, + + /** + * @param {number} x + * @param {number} y + * @return {module:zrender/core/PathProxy} + */ + moveTo: function (x, y) { + this.addData(CMD.M, x, y); + this._ctx && this._ctx.moveTo(x, y); + + // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用 + // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。 + // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要 + // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持 + this._x0 = x; + this._y0 = y; + + this._xi = x; + this._yi = y; + + return this; + }, + + /** + * @param {number} x + * @param {number} y + * @return {module:zrender/core/PathProxy} + */ + lineTo: function (x, y) { + var exceedUnit = mathAbs(x - this._xi) > this._ux + || mathAbs(y - this._yi) > this._uy + // Force draw the first segment + || this._len < 5; + + this.addData(CMD.L, x, y); + + if (this._ctx && exceedUnit) { + this._needsDash() ? this._dashedLineTo(x, y) + : this._ctx.lineTo(x, y); + } + if (exceedUnit) { + this._xi = x; + this._yi = y; + } + + return this; + }, + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @return {module:zrender/core/PathProxy} + */ + bezierCurveTo: function (x1, y1, x2, y2, x3, y3) { + this.addData(CMD.C, x1, y1, x2, y2, x3, y3); + if (this._ctx) { + this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) + : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); + } + this._xi = x3; + this._yi = y3; + return this; + }, + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {module:zrender/core/PathProxy} + */ + quadraticCurveTo: function (x1, y1, x2, y2) { + this.addData(CMD.Q, x1, y1, x2, y2); + if (this._ctx) { + this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) + : this._ctx.quadraticCurveTo(x1, y1, x2, y2); + } + this._xi = x2; + this._yi = y2; + return this; + }, + + /** + * @param {number} cx + * @param {number} cy + * @param {number} r + * @param {number} startAngle + * @param {number} endAngle + * @param {boolean} anticlockwise + * @return {module:zrender/core/PathProxy} + */ + arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) { + this.addData( + CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1 + ); + this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); + + this._xi = mathCos(endAngle) * r + cx; + this._xi = mathSin(endAngle) * r + cx; + return this; + }, + + // TODO + arcTo: function (x1, y1, x2, y2, radius) { + if (this._ctx) { + this._ctx.arcTo(x1, y1, x2, y2, radius); + } + return this; + }, + + // TODO + rect: function (x, y, w, h) { + this._ctx && this._ctx.rect(x, y, w, h); + this.addData(CMD.R, x, y, w, h); + return this; + }, + + /** + * @return {module:zrender/core/PathProxy} + */ + closePath: function () { + this.addData(CMD.Z); + + var ctx = this._ctx; + var x0 = this._x0; + var y0 = this._y0; + if (ctx) { + this._needsDash() && this._dashedLineTo(x0, y0); + ctx.closePath(); + } + + this._xi = x0; + this._yi = y0; + return this; + }, + + /** + * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。 + * stroke 同样 + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + fill: function (ctx) { + ctx && ctx.fill(); + this.toStatic(); + }, + + /** + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + stroke: function (ctx) { + ctx && ctx.stroke(); + this.toStatic(); + }, + + /** + * 必须在其它绘制命令前调用 + * Must be invoked before all other path drawing methods + * @return {module:zrender/core/PathProxy} + */ + setLineDash: function (lineDash) { + if (lineDash instanceof Array) { + this._lineDash = lineDash; + + this._dashIdx = 0; + + var lineDashSum = 0; + for (var i = 0; i < lineDash.length; i++) { + lineDashSum += lineDash[i]; + } + this._dashSum = lineDashSum; + } + return this; + }, + + /** + * 必须在其它绘制命令前调用 + * Must be invoked before all other path drawing methods + * @return {module:zrender/core/PathProxy} + */ + setLineDashOffset: function (offset) { + this._dashOffset = offset; + return this; + }, + + /** + * + * @return {boolean} + */ + len: function () { + return this._len; + }, + + /** + * 直接设置 Path 数据 + */ + setData: function (data) { + + var len = data.length; + + if (! (this.data && this.data.length == len) && hasTypedArray) { + this.data = new Float32Array(len); + } + + for (var i = 0; i < len; i++) { + this.data[i] = data[i]; + } + + this._len = len; + }, + + /** + * 添加子路径 + * @param {module:zrender/core/PathProxy|Array.} path + */ + appendPath: function (path) { + if (!(path instanceof Array)) { + path = [path]; + } + var len = path.length; + var appendSize = 0; + var offset = this._len; + for (var i = 0; i < len; i++) { + appendSize += path[i].len(); + } + if (hasTypedArray && (this.data instanceof Float32Array)) { + this.data = new Float32Array(offset + appendSize); + } + for (var i = 0; i < len; i++) { + var appendPathData = path[i].data; + for (var k = 0; k < appendPathData.length; k++) { + this.data[offset++] = appendPathData[k]; + } + } + this._len = offset; + }, + + /** + * 填充 Path 数据。 + * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。 + */ + addData: function (cmd) { + var data = this.data; + if (this._len + arguments.length > data.length) { + // 因为之前的数组已经转换成静态的 Float32Array + // 所以不够用时需要扩展一个新的动态数组 + this._expandData(); + data = this.data; + } + for (var i = 0; i < arguments.length; i++) { + data[this._len++] = arguments[i]; + } + + this._prevCmd = cmd; + }, + + _expandData: function () { + // Only if data is Float32Array + if (!(this.data instanceof Array)) { + var newData = []; + for (var i = 0; i < this._len; i++) { + newData[i] = this.data[i]; + } + this.data = newData; + } + }, + + /** + * If needs js implemented dashed line + * @return {boolean} + * @private + */ + _needsDash: function () { + return this._lineDash; + }, + + _dashedLineTo: function (x1, y1) { + var dashSum = this._dashSum; + var offset = this._dashOffset; + var lineDash = this._lineDash; + var ctx = this._ctx; + + var x0 = this._xi; + var y0 = this._yi; + var dx = x1 - x0; + var dy = y1 - y0; + var dist = mathSqrt(dx * dx + dy * dy); + var x = x0; + var y = y0; + var dash; + var nDash = lineDash.length; + var idx; + dx /= dist; + dy /= dist; + + if (offset < 0) { + // Convert to positive offset + offset = dashSum + offset; + } + offset %= dashSum; + x -= offset * dx; + y -= offset * dy; + + while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1) + || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) { + idx = this._dashIdx; + dash = lineDash[idx]; + x += dx * dash; + y += dy * dash; + this._dashIdx = (idx + 1) % nDash; + // Skip positive offset + if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) { + continue; + } + ctx[idx % 2 ? 'moveTo' : 'lineTo']( + dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), + dy >= 0 ? mathMin(y, y1) : mathMax(y, y1) + ); + } + // Offset for next lineTo + dx = x - x1; + dy = y - y1; + this._dashOffset = -mathSqrt(dx * dx + dy * dy); + }, + + // Not accurate dashed line to + _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) { + var dashSum = this._dashSum; + var offset = this._dashOffset; + var lineDash = this._lineDash; + var ctx = this._ctx; + + var x0 = this._xi; + var y0 = this._yi; + var t; + var dx; + var dy; + var cubicAt = curve.cubicAt; + var bezierLen = 0; + var idx = this._dashIdx; + var nDash = lineDash.length; + + var x; + var y; + + var tmpLen = 0; + + if (offset < 0) { + // Convert to positive offset + offset = dashSum + offset; + } + offset %= dashSum; + // Bezier approx length + for (t = 0; t < 1; t += 0.1) { + dx = cubicAt(x0, x1, x2, x3, t + 0.1) + - cubicAt(x0, x1, x2, x3, t); + dy = cubicAt(y0, y1, y2, y3, t + 0.1) + - cubicAt(y0, y1, y2, y3, t); + bezierLen += mathSqrt(dx * dx + dy * dy); + } + + // Find idx after add offset + for (; idx < nDash; idx++) { + tmpLen += lineDash[idx]; + if (tmpLen > offset) { + break; + } + } + t = (tmpLen - offset) / bezierLen; + + while (t <= 1) { + + x = cubicAt(x0, x1, x2, x3, t); + y = cubicAt(y0, y1, y2, y3, t); + + // Use line to approximate dashed bezier + // Bad result if dash is long + idx % 2 ? ctx.moveTo(x, y) + : ctx.lineTo(x, y); + + t += lineDash[idx] / bezierLen; + + idx = (idx + 1) % nDash; + } + + // Finish the last segment and calculate the new offset + (idx % 2 !== 0) && ctx.lineTo(x3, y3); + dx = x3 - x; + dy = y3 - y; + this._dashOffset = -mathSqrt(dx * dx + dy * dy); + }, + + _dashedQuadraticTo: function (x1, y1, x2, y2) { + // Convert quadratic to cubic using degree elevation + var x3 = x2; + var y3 = y2; + x2 = (x2 + 2 * x1) / 3; + y2 = (y2 + 2 * y1) / 3; + x1 = (this._xi + 2 * x1) / 3; + y1 = (this._yi + 2 * y1) / 3; + + this._dashedBezierTo(x1, y1, x2, y2, x3, y3); + }, + + /** + * 转成静态的 Float32Array 减少堆内存占用 + * Convert dynamic array to static Float32Array + */ + toStatic: function () { + var data = this.data; + if (data instanceof Array) { + data.length = this._len; + if (hasTypedArray) { + this.data = new Float32Array(data); + } + } + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function () { + min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE; + max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE; + + var data = this.data; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = data[i]; + yi = data[i + 1]; + + x0 = xi; + y0 = yi; + } + + switch (cmd) { + case CMD.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + min2[0] = x0; + min2[1] = y0; + max2[0] = x0; + max2[1] = y0; + break; + case CMD.L: + bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + bbox.fromCubic( + xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + min2, max2 + ); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.Q: + bbox.fromQuadratic( + xi, yi, data[i++], data[i++], data[i], data[i + 1], + min2, max2 + ); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.A: + // TODO Arc 判断的开销比较大 + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++]; + var endAngle = data[i++] + startAngle; + // TODO Arc 旋转 + var psi = data[i++]; + var anticlockwise = 1 - data[i++]; + + if (i == 1) { + // 直接使用 arc 命令 + // 第一个命令起点还未定义 + x0 = mathCos(startAngle) * rx + cx; + y0 = mathSin(startAngle) * ry + cy; + } + + bbox.fromArc( + cx, cy, rx, ry, startAngle, endAngle, + anticlockwise, min2, max2 + ); + + xi = mathCos(endAngle) * rx + cx; + yi = mathSin(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + // Use fromLine + bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2); + break; + case CMD.Z: + xi = x0; + yi = y0; + break; + } + + // Union + vec2.min(min, min, min2); + vec2.max(max, max, max2); + } + + // No data + if (i === 0) { + min[0] = min[1] = max[0] = max[1] = 0; + } + + return new BoundingRect( + min[0], min[1], max[0] - min[0], max[1] - min[1] + ); + }, + + /** + * Rebuild path from current data + * Rebuild path will not consider javascript implemented line dash. + * @param {CanvasRenderingContext} ctx + */ + rebuildPath: function (ctx) { + var d = this.data; + var x0, y0; + var xi, yi; + var x, y; + var ux = this._ux; + var uy = this._uy; + var len = this._len; + for (var i = 0; i < len;) { + var cmd = d[i++]; + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = d[i]; + yi = d[i + 1]; + + x0 = xi; + y0 = yi; + } + switch (cmd) { + case CMD.M: + x0 = xi = d[i++]; + y0 = yi = d[i++]; + ctx.moveTo(xi, yi); + break; + case CMD.L: + x = d[i++]; + y = d[i++]; + // Not draw too small seg between + if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) { + ctx.lineTo(x, y); + xi = x; + yi = y; + } + break; + case CMD.C: + ctx.bezierCurveTo( + d[i++], d[i++], d[i++], d[i++], d[i++], d[i++] + ); + xi = d[i - 2]; + yi = d[i - 1]; + break; + case CMD.Q: + ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]); + xi = d[i - 2]; + yi = d[i - 1]; + break; + case CMD.A: + var cx = d[i++]; + var cy = d[i++]; + var rx = d[i++]; + var ry = d[i++]; + var theta = d[i++]; + var dTheta = d[i++]; + var psi = d[i++]; + var fs = d[i++]; + var r = (rx > ry) ? rx : ry; + var scaleX = (rx > ry) ? 1 : rx / ry; + var scaleY = (rx > ry) ? ry / rx : 1; + var isEllipse = Math.abs(rx - ry) > 1e-3; + var endAngle = theta + dTheta; + if (isEllipse) { + ctx.translate(cx, cy); + ctx.rotate(psi); + ctx.scale(scaleX, scaleY); + ctx.arc(0, 0, r, theta, endAngle, 1 - fs); + ctx.scale(1 / scaleX, 1 / scaleY); + ctx.rotate(-psi); + ctx.translate(-cx, -cy); + } + else { + ctx.arc(cx, cy, r, theta, endAngle, 1 - fs); + } + + if (i == 1) { + // 直接使用 arc 命令 + // 第一个命令起点还未定义 + x0 = mathCos(theta) * rx + cx; + y0 = mathSin(theta) * ry + cy; + } + xi = mathCos(endAngle) * rx + cx; + yi = mathSin(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = d[i]; + y0 = yi = d[i + 1]; + ctx.rect(d[i++], d[i++], d[i++], d[i++]); + break; + case CMD.Z: + ctx.closePath(); + xi = x0; + yi = y0; + } + } + } + }; + + PathProxy.CMD = CMD; + + module.exports = PathProxy; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 曲线辅助模块 + * @module zrender/core/curve + * @author pissang(https://www.github.com/pissang) + */ + + + var vec2 = __webpack_require__(10); + var v2Create = vec2.create; + var v2DistSquare = vec2.distSquare; + var mathPow = Math.pow; + var mathSqrt = Math.sqrt; + + var EPSILON = 1e-8; + var EPSILON_NUMERIC = 1e-4; + + var THREE_SQRT = mathSqrt(3); + var ONE_THIRD = 1 / 3; + + // 临时变量 + var _v0 = v2Create(); + var _v1 = v2Create(); + var _v2 = v2Create(); + // var _v3 = vec2.create(); + + function isAroundZero(val) { + return val > -EPSILON && val < EPSILON; + } + function isNotAroundZero(val) { + return val > EPSILON || val < -EPSILON; + } + /** + * 计算三次贝塞尔值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @return {number} + */ + function cubicAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return onet * onet * (onet * p0 + 3 * t * p1) + + t * t * (t * p3 + 3 * onet * p2); + } + + /** + * 计算三次贝塞尔导数值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @return {number} + */ + function cubicDerivativeAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return 3 * ( + ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + + (p3 - p2) * t * t + ); + } + + /** + * 计算三次贝塞尔方程根,使用盛金公式 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} val + * @param {Array.} roots + * @return {number} 有效根数目 + */ + function cubicRootAt(p0, p1, p2, p3, val, roots) { + // Evaluate roots of cubic functions + var a = p3 + 3 * (p1 - p2) - p0; + var b = 3 * (p2 - p1 * 2 + p0); + var c = 3 * (p1 - p0); + var d = p0 - val; + + var A = b * b - 3 * a * c; + var B = b * c - 9 * a * d; + var C = c * c - 3 * b * d; + + var n = 0; + + if (isAroundZero(A) && isAroundZero(B)) { + if (isAroundZero(b)) { + roots[0] = 0; + } + else { + var t1 = -c / b; //t1, t2, t3, b is not zero + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = B * B - 4 * A * C; + + if (isAroundZero(disc)) { + var K = B / A; + var t1 = -b / a + K; // t1, a is not zero + var t2 = -K / 2; // t2, t3 + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var Y1 = A * b + 1.5 * a * (-B + discSqrt); + var Y2 = A * b + 1.5 * a * (-B - discSqrt); + if (Y1 < 0) { + Y1 = -mathPow(-Y1, ONE_THIRD); + } + else { + Y1 = mathPow(Y1, ONE_THIRD); + } + if (Y2 < 0) { + Y2 = -mathPow(-Y2, ONE_THIRD); + } + else { + Y2 = mathPow(Y2, ONE_THIRD); + } + var t1 = (-b - (Y1 + Y2)) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else { + var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A)); + var theta = Math.acos(T) / 3; + var ASqrt = mathSqrt(A); + var tmp = Math.cos(theta); + + var t1 = (-b - 2 * ASqrt * tmp) / (3 * a); + var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a); + var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + if (t3 >= 0 && t3 <= 1) { + roots[n++] = t3; + } + } + } + return n; + } + + /** + * 计算三次贝塞尔方程极限值的位置 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {Array.} extrema + * @return {number} 有效数目 + */ + function cubicExtrema(p0, p1, p2, p3, extrema) { + var b = 6 * p2 - 12 * p1 + 6 * p0; + var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2; + var c = 3 * p1 - 3 * p0; + + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <=1) { + extrema[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + extrema[0] = -b / (2 * a); + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + extrema[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + extrema[n++] = t2; + } + } + } + return n; + } + + /** + * 细分三次贝塞尔曲线 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @param {Array.} out + */ + function cubicSubdivide(p0, p1, p2, p3, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p23 = (p3 - p2) * t + p2; + + var p012 = (p12 - p01) * t + p01; + var p123 = (p23 - p12) * t + p12; + + var p0123 = (p123 - p012) * t + p012; + // Seg0 + out[0] = p0; + out[1] = p01; + out[2] = p012; + out[3] = p0123; + // Seg1 + out[4] = p0123; + out[5] = p123; + out[6] = p23; + out[7] = p3; + } + + /** + * 投射点到三次贝塞尔曲线上,返回投射距离。 + * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {number} x + * @param {number} y + * @param {Array.} [out] 投射点 + * @return {number} + */ + function cubicProjectPoint( + x0, y0, x1, y1, x2, y2, x3, y3, + x, y, out + ) { + // http://pomax.github.io/bezierinfo/#projections + var t; + var interval = 0.005; + var d = Infinity; + var prev; + var next; + var d1; + var d2; + + _v0[0] = x; + _v0[1] = y; + + // 先粗略估计一下可能的最小距离的 t 值 + // PENDING + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = cubicAt(x0, x1, x2, x3, _t); + _v1[1] = cubicAt(y0, y1, y2, y3, _t); + d1 = v2DistSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + + // At most 32 iteration + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + prev = t - interval; + next = t + interval; + // t - interval + _v1[0] = cubicAt(x0, x1, x2, x3, prev); + _v1[1] = cubicAt(y0, y1, y2, y3, prev); + + d1 = v2DistSquare(_v1, _v0); + + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + // t + interval + _v2[0] = cubicAt(x0, x1, x2, x3, next); + _v2[1] = cubicAt(y0, y1, y2, y3, next); + d2 = v2DistSquare(_v2, _v0); + + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + // t + if (out) { + out[0] = cubicAt(x0, x1, x2, x3, t); + out[1] = cubicAt(y0, y1, y2, y3, t); + } + // console.log(interval, i); + return mathSqrt(d); + } + + /** + * 计算二次方贝塞尔值 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @return {number} + */ + function quadraticAt(p0, p1, p2, t) { + var onet = 1 - t; + return onet * (onet * p0 + 2 * t * p1) + t * t * p2; + } + + /** + * 计算二次方贝塞尔导数值 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @return {number} + */ + function quadraticDerivativeAt(p0, p1, p2, t) { + return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); + } + + /** + * 计算二次方贝塞尔方程根 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @param {Array.} roots + * @return {number} 有效根数目 + */ + function quadraticRootAt(p0, p1, p2, val, roots) { + var a = p0 - 2 * p1 + p2; + var b = 2 * (p1 - p0); + var c = p0 - val; + + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + var t1 = -b / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + } + return n; + } + + /** + * 计算二次贝塞尔方程极限值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @return {number} + */ + function quadraticExtremum(p0, p1, p2) { + var divider = p0 + p2 - 2 * p1; + if (divider === 0) { + // p1 is center of p0 and p2 + return 0.5; + } + else { + return (p0 - p1) / divider; + } + } + + /** + * 细分二次贝塞尔曲线 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @param {Array.} out + */ + function quadraticSubdivide(p0, p1, p2, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p012 = (p12 - p01) * t + p01; + + // Seg0 + out[0] = p0; + out[1] = p01; + out[2] = p012; + + // Seg1 + out[3] = p012; + out[4] = p12; + out[5] = p2; + } + + /** + * 投射点到二次贝塞尔曲线上,返回投射距离。 + * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x + * @param {number} y + * @param {Array.} out 投射点 + * @return {number} + */ + function quadraticProjectPoint( + x0, y0, x1, y1, x2, y2, + x, y, out + ) { + // http://pomax.github.io/bezierinfo/#projections + var t; + var interval = 0.005; + var d = Infinity; + + _v0[0] = x; + _v0[1] = y; + + // 先粗略估计一下可能的最小距离的 t 值 + // PENDING + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = quadraticAt(x0, x1, x2, _t); + _v1[1] = quadraticAt(y0, y1, y2, _t); + var d1 = v2DistSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + + // At most 32 iteration + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + var prev = t - interval; + var next = t + interval; + // t - interval + _v1[0] = quadraticAt(x0, x1, x2, prev); + _v1[1] = quadraticAt(y0, y1, y2, prev); + + var d1 = v2DistSquare(_v1, _v0); + + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + // t + interval + _v2[0] = quadraticAt(x0, x1, x2, next); + _v2[1] = quadraticAt(y0, y1, y2, next); + var d2 = v2DistSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + // t + if (out) { + out[0] = quadraticAt(x0, x1, x2, t); + out[1] = quadraticAt(y0, y1, y2, t); + } + // console.log(interval, i); + return mathSqrt(d); + } + + module.exports = { + + cubicAt: cubicAt, + + cubicDerivativeAt: cubicDerivativeAt, + + cubicRootAt: cubicRootAt, + + cubicExtrema: cubicExtrema, + + cubicSubdivide: cubicSubdivide, + + cubicProjectPoint: cubicProjectPoint, + + quadraticAt: quadraticAt, + + quadraticDerivativeAt: quadraticDerivativeAt, + + quadraticRootAt: quadraticRootAt, + + quadraticExtremum: quadraticExtremum, + + quadraticSubdivide: quadraticSubdivide, + + quadraticProjectPoint: quadraticProjectPoint + }; + + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @author Yi Shen(https://github.com/pissang) + */ + + + var vec2 = __webpack_require__(10); + var curve = __webpack_require__(50); + + var bbox = {}; + var mathMin = Math.min; + var mathMax = Math.max; + var mathSin = Math.sin; + var mathCos = Math.cos; + + var start = vec2.create(); + var end = vec2.create(); + var extremity = vec2.create(); + + var PI2 = Math.PI * 2; + /** + * 从顶点数组中计算出最小包围盒,写入`min`和`max`中 + * @module zrender/core/bbox + * @param {Array} points 顶点数组 + * @param {number} min + * @param {number} max + */ + bbox.fromPoints = function(points, min, max) { + if (points.length === 0) { + return; + } + var p = points[0]; + var left = p[0]; + var right = p[0]; + var top = p[1]; + var bottom = p[1]; + var i; + + for (i = 1; i < points.length; i++) { + p = points[i]; + left = mathMin(left, p[0]); + right = mathMax(right, p[0]); + top = mathMin(top, p[1]); + bottom = mathMax(bottom, p[1]); + } + + min[0] = left; + min[1] = top; + max[0] = right; + max[1] = bottom; + }; + + /** + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromLine = function (x0, y0, x1, y1, min, max) { + min[0] = mathMin(x0, x1); + min[1] = mathMin(y0, y1); + max[0] = mathMax(x0, x1); + max[1] = mathMax(y0, y1); + }; + + var xDim = []; + var yDim = []; + /** + * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中 + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromCubic = function( + x0, y0, x1, y1, x2, y2, x3, y3, min, max + ) { + var cubicExtrema = curve.cubicExtrema; + var cubicAt = curve.cubicAt; + var i; + var n = cubicExtrema(x0, x1, x2, x3, xDim); + min[0] = Infinity; + min[1] = Infinity; + max[0] = -Infinity; + max[1] = -Infinity; + + for (i = 0; i < n; i++) { + var x = cubicAt(x0, x1, x2, x3, xDim[i]); + min[0] = mathMin(x, min[0]); + max[0] = mathMax(x, max[0]); + } + n = cubicExtrema(y0, y1, y2, y3, yDim); + for (i = 0; i < n; i++) { + var y = cubicAt(y0, y1, y2, y3, yDim[i]); + min[1] = mathMin(y, min[1]); + max[1] = mathMax(y, max[1]); + } + + min[0] = mathMin(x0, min[0]); + max[0] = mathMax(x0, max[0]); + min[0] = mathMin(x3, min[0]); + max[0] = mathMax(x3, max[0]); + + min[1] = mathMin(y0, min[1]); + max[1] = mathMax(y0, max[1]); + min[1] = mathMin(y3, min[1]); + max[1] = mathMax(y3, max[1]); + }; + + /** + * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中 + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromQuadratic = function(x0, y0, x1, y1, x2, y2, min, max) { + var quadraticExtremum = curve.quadraticExtremum; + var quadraticAt = curve.quadraticAt; + // Find extremities, where derivative in x dim or y dim is zero + var tx = + mathMax( + mathMin(quadraticExtremum(x0, x1, x2), 1), 0 + ); + var ty = + mathMax( + mathMin(quadraticExtremum(y0, y1, y2), 1), 0 + ); + + var x = quadraticAt(x0, x1, x2, tx); + var y = quadraticAt(y0, y1, y2, ty); + + min[0] = mathMin(x0, x2, x); + min[1] = mathMin(y0, y2, y); + max[0] = mathMax(x0, x2, x); + max[1] = mathMax(y0, y2, y); + }; + + /** + * 从圆弧中计算出最小包围盒,写入`min`和`max`中 + * @method + * @memberOf module:zrender/core/bbox + * @param {number} x + * @param {number} y + * @param {number} rx + * @param {number} ry + * @param {number} startAngle + * @param {number} endAngle + * @param {number} anticlockwise + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromArc = function ( + x, y, rx, ry, startAngle, endAngle, anticlockwise, min, max + ) { + var vec2Min = vec2.min; + var vec2Max = vec2.max; + + var diff = Math.abs(startAngle - endAngle); + + + if (diff % PI2 < 1e-4 && diff > 1e-4) { + // Is a circle + min[0] = x - rx; + min[1] = y - ry; + max[0] = x + rx; + max[1] = y + ry; + return; + } + + start[0] = mathCos(startAngle) * rx + x; + start[1] = mathSin(startAngle) * ry + y; + + end[0] = mathCos(endAngle) * rx + x; + end[1] = mathSin(endAngle) * ry + y; + + vec2Min(min, start, end); + vec2Max(max, start, end); + + // Thresh to [0, Math.PI * 2] + startAngle = startAngle % (PI2); + if (startAngle < 0) { + startAngle = startAngle + PI2; + } + endAngle = endAngle % (PI2); + if (endAngle < 0) { + endAngle = endAngle + PI2; + } + + if (startAngle > endAngle && !anticlockwise) { + endAngle += PI2; + } + else if (startAngle < endAngle && anticlockwise) { + startAngle += PI2; + } + if (anticlockwise) { + var tmp = endAngle; + endAngle = startAngle; + startAngle = tmp; + } + + // var number = 0; + // var step = (anticlockwise ? -Math.PI : Math.PI) / 2; + for (var angle = 0; angle < endAngle; angle += Math.PI / 2) { + if (angle > startAngle) { + extremity[0] = mathCos(angle) * rx + x; + extremity[1] = mathSin(angle) * ry + y; + + vec2Min(min, extremity, min); + vec2Max(max, extremity, max); + } + } + }; + + module.exports = bbox; + + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var CMD = __webpack_require__(49).CMD; + var line = __webpack_require__(53); + var cubic = __webpack_require__(54); + var quadratic = __webpack_require__(55); + var arc = __webpack_require__(56); + var normalizeRadian = __webpack_require__(57).normalizeRadian; + var curve = __webpack_require__(50); + + var windingLine = __webpack_require__(58); + + var containStroke = line.containStroke; + + var PI2 = Math.PI * 2; + + var EPSILON = 1e-4; + + function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON; + } + + // 临时数组 + var roots = [-1, -1, -1]; + var extrema = [-1, -1]; + + function swapExtrema() { + var tmp = extrema[0]; + extrema[0] = extrema[1]; + extrema[1] = tmp; + } + + function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) { + // Quick reject + if ( + (y > y0 && y > y1 && y > y2 && y > y3) + || (y < y0 && y < y1 && y < y2 && y < y3) + ) { + return 0; + } + var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var w = 0; + var nExtrema = -1; + var y0_, y1_; + for (var i = 0; i < nRoots; i++) { + var t = roots[i]; + + // Avoid winding error when intersection point is the connect point of two line of polygon + var unit = (t === 0 || t === 1) ? 0.5 : 1; + + var x_ = curve.cubicAt(x0, x1, x2, x3, t); + if (x_ < x) { // Quick reject + continue; + } + if (nExtrema < 0) { + nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema); + if (extrema[1] < extrema[0] && nExtrema > 1) { + swapExtrema(); + } + y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]); + if (nExtrema > 1) { + y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]); + } + } + if (nExtrema == 2) { + // 分成三段单调函数 + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else if (t < extrema[1]) { + w += y1_ < y0_ ? unit : -unit; + } + else { + w += y3 < y1_ ? unit : -unit; + } + } + else { + // 分成两段单调函数 + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else { + w += y3 < y0_ ? unit : -unit; + } + } + } + return w; + } + } + + function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) { + // Quick reject + if ( + (y > y0 && y > y1 && y > y2) + || (y < y0 && y < y1 && y < y2) + ) { + return 0; + } + var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var t = curve.quadraticExtremum(y0, y1, y2); + if (t >= 0 && t <= 1) { + var w = 0; + var y_ = curve.quadraticAt(y0, y1, y2, t); + for (var i = 0; i < nRoots; i++) { + // Remove one endpoint. + var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1; + + var x_ = curve.quadraticAt(x0, x1, x2, roots[i]); + if (x_ < x) { // Quick reject + continue; + } + if (roots[i] < t) { + w += y_ < y0 ? unit : -unit; + } + else { + w += y2 < y_ ? unit : -unit; + } + } + return w; + } + else { + // Remove one endpoint. + var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1; + + var x_ = curve.quadraticAt(x0, x1, x2, roots[0]); + if (x_ < x) { // Quick reject + return 0; + } + return y2 < y0 ? unit : -unit; + } + } + } + + // TODO + // Arc 旋转 + function windingArc( + cx, cy, r, startAngle, endAngle, anticlockwise, x, y + ) { + y -= cy; + if (y > r || y < -r) { + return 0; + } + var tmp = Math.sqrt(r * r - y * y); + roots[0] = -tmp; + roots[1] = tmp; + + var diff = Math.abs(startAngle - endAngle); + if (diff < 1e-4) { + return 0; + } + if (diff % PI2 < 1e-4) { + // Is a circle + startAngle = 0; + endAngle = PI2; + var dir = anticlockwise ? 1 : -1; + if (x >= roots[0] + cx && x <= roots[1] + cx) { + return dir; + } else { + return 0; + } + } + + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } + else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2; + } + + var w = 0; + for (var i = 0; i < 2; i++) { + var x_ = roots[i]; + if (x_ + cx > x) { + var angle = Math.atan2(y, x_); + var dir = anticlockwise ? 1 : -1; + if (angle < 0) { + angle = PI2 + angle; + } + if ( + (angle >= startAngle && angle <= endAngle) + || (angle + PI2 >= startAngle && angle + PI2 <= endAngle) + ) { + if (angle > Math.PI / 2 && angle < Math.PI * 1.5) { + dir = -dir; + } + w += dir; + } + } + } + return w; + } + + function containPath(data, lineWidth, isStroke, x, y) { + var w = 0; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + // Begin a new subpath + if (cmd === CMD.M && i > 1) { + // Close previous subpath + if (!isStroke) { + w += windingLine(xi, yi, x0, y0, x, y); + } + // 如果被任何一个 subpath 包含 + // if (w !== 0) { + // return true; + // } + } + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = data[i]; + yi = data[i + 1]; + + x0 = xi; + y0 = yi; + } + + switch (cmd) { + case CMD.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + break; + case CMD.L: + if (isStroke) { + if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) { + return true; + } + } + else { + // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN + w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + if (isStroke) { + if (cubic.containStroke(xi, yi, + data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + lineWidth, x, y + )) { + return true; + } + } + else { + w += windingCubic( + xi, yi, + data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + x, y + ) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.Q: + if (isStroke) { + if (quadratic.containStroke(xi, yi, + data[i++], data[i++], data[i], data[i + 1], + lineWidth, x, y + )) { + return true; + } + } + else { + w += windingQuadratic( + xi, yi, + data[i++], data[i++], data[i], data[i + 1], + x, y + ) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.A: + // TODO Arc 判断的开销比较大 + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var theta = data[i++]; + var dTheta = data[i++]; + // TODO Arc 旋转 + var psi = data[i++]; + var anticlockwise = 1 - data[i++]; + var x1 = Math.cos(theta) * rx + cx; + var y1 = Math.sin(theta) * ry + cy; + // 不是直接使用 arc 命令 + if (i > 1) { + w += windingLine(xi, yi, x1, y1, x, y); + } + else { + // 第一个命令起点还未定义 + x0 = x1; + y0 = y1; + } + // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放 + var _x = (x - cx) * ry / rx + cx; + if (isStroke) { + if (arc.containStroke( + cx, cy, ry, theta, theta + dTheta, anticlockwise, + lineWidth, _x, y + )) { + return true; + } + } + else { + w += windingArc( + cx, cy, ry, theta, theta + dTheta, anticlockwise, + _x, y + ); + } + xi = Math.cos(theta + dTheta) * rx + cx; + yi = Math.sin(theta + dTheta) * ry + cy; + break; + case CMD.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + var x1 = x0 + width; + var y1 = y0 + height; + if (isStroke) { + if (containStroke(x0, y0, x1, y0, lineWidth, x, y) + || containStroke(x1, y0, x1, y1, lineWidth, x, y) + || containStroke(x1, y1, x0, y1, lineWidth, x, y) + || containStroke(x0, y1, x0, y0, lineWidth, x, y) + ) { + return true; + } + } + else { + // FIXME Clockwise ? + w += windingLine(x1, y0, x1, y1, x, y); + w += windingLine(x0, y1, x0, y0, x, y); + } + break; + case CMD.Z: + if (isStroke) { + if (containStroke( + xi, yi, x0, y0, lineWidth, x, y + )) { + return true; + } + } + else { + // Close a subpath + w += windingLine(xi, yi, x0, y0, x, y); + // 如果被任何一个 subpath 包含 + // FIXME subpaths may overlap + // if (w !== 0) { + // return true; + // } + } + xi = x0; + yi = y0; + break; + } + } + if (!isStroke && !isAroundEqual(yi, y0)) { + w += windingLine(xi, yi, x0, y0, x, y) || 0; + } + return w !== 0; + } + + module.exports = { + contain: function (pathData, x, y) { + return containPath(pathData, 0, false, x, y); + }, + + containStroke: function (pathData, lineWidth, x, y) { + return containPath(pathData, lineWidth, true, x, y); + } + }; + + +/***/ }, +/* 53 */ +/***/ function(module, exports) { + + + module.exports = { + /** + * 线段包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function (x0, y0, x1, y1, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + var _a = 0; + var _b = x0; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l) + || (y < y0 - _l && y < y1 - _l) + || (x > x0 + _l && x > x1 + _l) + || (x < x0 - _l && x < x1 - _l) + ) { + return false; + } + + if (x0 !== x1) { + _a = (y0 - y1) / (x0 - x1); + _b = (x0 * y1 - x1 * y0) / (x0 - x1) ; + } + else { + return Math.abs(x - x0) <= _l / 2; + } + var tmp = _a * x - y + _b; + var _s = tmp * tmp / (_a * _a + 1); + return _s <= _l / 2 * _l / 2; + } + }; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curve = __webpack_require__(50); + + module.exports = { + /** + * 三次贝塞尔曲线描边包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) + ) { + return false; + } + var d = curve.cubicProjectPoint( + x0, y0, x1, y1, x2, y2, x3, y3, + x, y, null + ); + return d <= _l / 2; + } + }; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curve = __webpack_require__(50); + + module.exports = { + /** + * 二次贝塞尔曲线描边包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function (x0, y0, x1, y1, x2, y2, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l) + ) { + return false; + } + var d = curve.quadraticProjectPoint( + x0, y0, x1, y1, x2, y2, + x, y, null + ); + return d <= _l / 2; + } + }; + + +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + + + + var normalizeRadian = __webpack_require__(57).normalizeRadian; + var PI2 = Math.PI * 2; + + module.exports = { + /** + * 圆弧描边包含判断 + * @param {number} cx + * @param {number} cy + * @param {number} r + * @param {number} startAngle + * @param {number} endAngle + * @param {boolean} anticlockwise + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {Boolean} + */ + containStroke: function ( + cx, cy, r, startAngle, endAngle, anticlockwise, + lineWidth, x, y + ) { + + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + + x -= cx; + y -= cy; + var d = Math.sqrt(x * x + y * y); + + if ((d - _l > r) || (d + _l < r)) { + return false; + } + if (Math.abs(startAngle - endAngle) % PI2 < 1e-4) { + // Is a circle + return true; + } + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2; + } + + var angle = Math.atan2(y, x); + if (angle < 0) { + angle += PI2; + } + return (angle >= startAngle && angle <= endAngle) + || (angle + PI2 >= startAngle && angle + PI2 <= endAngle); + } + }; + + +/***/ }, +/* 57 */ +/***/ function(module, exports) { + + + + var PI2 = Math.PI * 2; + module.exports = { + normalizeRadian: function(angle) { + angle %= PI2; + if (angle < 0) { + angle += PI2; + } + return angle; + } + }; + + +/***/ }, +/* 58 */ +/***/ function(module, exports) { + + + module.exports = function windingLine(x0, y0, x1, y1, x, y) { + if ((y > y0 && y > y1) || (y < y0 && y < y1)) { + return 0; + } + // Ignore horizontal line + if (y1 === y0) { + return 0; + } + var dir = y1 < y0 ? 1 : -1; + var t = (y - y0) / (y1 - y0); + + // Avoid winding error when intersection point is the connect point of two line of polygon + if (t === 1 || t === 0) { + dir = y1 < y0 ? 0.5 : -0.5; + } + + var x_ = t * (x1 - x0) + x0; + + return x_ > x ? dir : 0; + }; + + +/***/ }, +/* 59 */ +/***/ function(module, exports) { + + + + var Pattern = function (image, repeat) { + this.image = image; + this.repeat = repeat; + + // Can be cloned + this.type = 'pattern'; + }; + + Pattern.prototype.getCanvasPattern = function (ctx) { + + return this._canvasPattern + || (this._canvasPattern = ctx.createPattern(this.image, this.repeat)); + }; + + module.exports = Pattern; + + +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { + + + + var CMD = __webpack_require__(49).CMD; + var vec2 = __webpack_require__(10); + var v2ApplyTransform = vec2.applyTransform; + + var points = [[], [], []]; + var mathSqrt = Math.sqrt; + var mathAtan2 = Math.atan2; + function transformPath(path, m) { + var data = path.data; + var cmd; + var nPoint; + var i; + var j; + var k; + var p; + + var M = CMD.M; + var C = CMD.C; + var L = CMD.L; + var R = CMD.R; + var A = CMD.A; + var Q = CMD.Q; + + for (i = 0, j = 0; i < data.length;) { + cmd = data[i++]; + j = i; + nPoint = 0; + + switch (cmd) { + case M: + nPoint = 1; + break; + case L: + nPoint = 1; + break; + case C: + nPoint = 3; + break; + case Q: + nPoint = 2; + break; + case A: + var x = m[4]; + var y = m[5]; + var sx = mathSqrt(m[0] * m[0] + m[1] * m[1]); + var sy = mathSqrt(m[2] * m[2] + m[3] * m[3]); + var angle = mathAtan2(-m[1] / sy, m[0] / sx); + // cx + data[i++] += x; + // cy + data[i++] += y; + // Scale rx and ry + // FIXME Assume psi is 0 here + data[i++] *= sx; + data[i++] *= sy; + + // Start angle + data[i++] += angle; + // end angle + data[i++] += angle; + // FIXME psi + i += 2; + j = i; + break; + case R: + // x0, y0 + p[0] = data[i++]; + p[1] = data[i++]; + v2ApplyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + // x1, y1 + p[0] += data[i++]; + p[1] += data[i++]; + v2ApplyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + } + + for (k = 0; k < nPoint; k++) { + var p = points[k]; + p[0] = data[i++]; + p[1] = data[i++]; + + v2ApplyTransform(p, p, m); + // Write back + data[j++] = p[0]; + data[j++] = p[1]; + } + } + } + + module.exports = transformPath; + + +/***/ }, +/* 61 */ +/***/ function(module, exports) { + + + + /** + * @param {Array.} colorStops + */ + var Gradient = function (colorStops) { + + this.colorStops = colorStops || []; + }; + + Gradient.prototype = { + + constructor: Gradient, + + addColorStop: function (offset, color) { + this.colorStops.push({ + + offset: offset, + + color: color + }); + } + }; + + module.exports = Gradient; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Image element + * @module zrender/graphic/Image + */ + + + + var Displayable = __webpack_require__(46); + var BoundingRect = __webpack_require__(9); + var zrUtil = __webpack_require__(4); + + var LRU = __webpack_require__(63); + var globalImageCache = new LRU(50); + /** + * @alias zrender/graphic/Image + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + function ZImage(opts) { + Displayable.call(this, opts); + } + + ZImage.prototype = { + + constructor: ZImage, + + type: 'image', + + brush: function (ctx, prevEl) { + var style = this.style; + var src = style.image; + var image; + + // Must bind each time + style.bind(ctx, this, prevEl); + // style.image is a url string + if (typeof src === 'string') { + image = this._image; + } + // style.image is an HTMLImageElement or HTMLCanvasElement or Canvas + else { + image = src; + } + // FIXME Case create many images with src + if (!image && src) { + // Try get from global image cache + var cachedImgObj = globalImageCache.get(src); + if (!cachedImgObj) { + // Create a new image + image = new Image(); + image.onload = function () { + image.onload = null; + for (var i = 0; i < cachedImgObj.pending.length; i++) { + cachedImgObj.pending[i].dirty(); + } + }; + cachedImgObj = { + image: image, + pending: [this] + }; + image.src = src; + globalImageCache.put(src, cachedImgObj); + this._image = image; + return; + } + else { + image = cachedImgObj.image; + this._image = image; + // Image is not complete finish, add to pending list + if (!image.width || !image.height) { + cachedImgObj.pending.push(this); + return; + } + } + } + + if (image) { + // 图片已经加载完成 + // if (image.nodeName.toUpperCase() == 'IMG') { + // if (!image.complete) { + // return; + // } + // } + // Else is canvas + + var width = style.width || image.width; + var height = style.height || image.height; + var x = style.x || 0; + var y = style.y || 0; + // 图片加载失败 + if (!image.width || !image.height) { + return; + } + + // 设置transform + this.setTransform(ctx); + + + if (style.sWidth && style.sHeight) { + var sx = style.sx || 0; + var sy = style.sy || 0; + ctx.drawImage( + image, + sx, sy, style.sWidth, style.sHeight, + x, y, width, height + ); + } + else if (style.sx && style.sy) { + var sx = style.sx; + var sy = style.sy; + var sWidth = width - sx; + var sHeight = height - sy; + ctx.drawImage( + image, + sx, sy, sWidth, sHeight, + x, y, width, height + ); + } + else { + ctx.drawImage(image, x, y, width, height); + } + + // 如果没设置宽和高的话自动根据图片宽高设置 + if (style.width == null) { + style.width = width; + } + if (style.height == null) { + style.height = height; + } + + this.restoreTransform(ctx); + + // Draw rect text + if (style.text != null) { + this.drawRectText(ctx, this.getBoundingRect()); + } + + } + }, + + getBoundingRect: function () { + var style = this.style; + if (! this._rect) { + this._rect = new BoundingRect( + style.x || 0, style.y || 0, style.width || 0, style.height || 0 + ); + } + return this._rect; + } + }; + + zrUtil.inherits(ZImage, Displayable); + + module.exports = ZImage; + + +/***/ }, +/* 63 */ +/***/ function(module, exports) { + + // Simple LRU cache use doubly linked list + // @module zrender/core/LRU + + + /** + * Simple double linked list. Compared with array, it has O(1) remove operation. + * @constructor + */ + var LinkedList = function() { + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.head = null; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.tail = null; + + this._len = 0; + }; + + var linkedListProto = LinkedList.prototype; + /** + * Insert a new value at the tail + * @param {} val + * @return {module:zrender/core/LRU~Entry} + */ + linkedListProto.insert = function(val) { + var entry = new Entry(val); + this.insertEntry(entry); + return entry; + }; + + /** + * Insert an entry at the tail + * @param {module:zrender/core/LRU~Entry} entry + */ + linkedListProto.insertEntry = function(entry) { + if (!this.head) { + this.head = this.tail = entry; + } + else { + this.tail.next = entry; + entry.prev = this.tail; + this.tail = entry; + } + this._len++; + }; + + /** + * Remove entry. + * @param {module:zrender/core/LRU~Entry} entry + */ + linkedListProto.remove = function(entry) { + var prev = entry.prev; + var next = entry.next; + if (prev) { + prev.next = next; + } + else { + // Is head + this.head = next; + } + if (next) { + next.prev = prev; + } + else { + // Is tail + this.tail = prev; + } + entry.next = entry.prev = null; + this._len--; + }; + + /** + * @return {number} + */ + linkedListProto.len = function() { + return this._len; + }; + + /** + * @constructor + * @param {} val + */ + var Entry = function(val) { + /** + * @type {} + */ + this.value = val; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.next; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.prev; + }; + + /** + * LRU Cache + * @constructor + * @alias module:zrender/core/LRU + */ + var LRU = function(maxSize) { + + this._list = new LinkedList(); + + this._map = {}; + + this._maxSize = maxSize || 10; + }; + + var LRUProto = LRU.prototype; + + /** + * @param {string} key + * @param {} value + */ + LRUProto.put = function(key, value) { + var list = this._list; + var map = this._map; + if (map[key] == null) { + var len = list.len(); + if (len >= this._maxSize && len > 0) { + // Remove the least recently used + var leastUsedEntry = list.head; + list.remove(leastUsedEntry); + delete map[leastUsedEntry.key]; + } + + var entry = list.insert(value); + entry.key = key; + map[key] = entry; + } + }; + + /** + * @param {string} key + * @return {} + */ + LRUProto.get = function(key) { + var entry = this._map[key]; + var list = this._list; + if (entry != null) { + // Put the latest used entry in the tail + if (entry !== list.tail) { + list.remove(entry); + list.insertEntry(entry); + } + + return entry.value; + } + }; + + /** + * Clear the cache + */ + LRUProto.clear = function() { + this._list.clear(); + this._map = {}; + }; + + module.exports = LRU; + + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Text element + * @module zrender/graphic/Text + * + * TODO Wrapping + * + * Text not support gradient + */ + + + + var Displayable = __webpack_require__(46); + var zrUtil = __webpack_require__(4); + var textContain = __webpack_require__(8); + + /** + * @alias zrender/graphic/Text + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + var Text = function (opts) { + Displayable.call(this, opts); + }; + + Text.prototype = { + + constructor: Text, + + type: 'text', + + brush: function (ctx, prevEl) { + var style = this.style; + var x = style.x || 0; + var y = style.y || 0; + // Convert to string + var text = style.text; + + // Convert to string + text != null && (text += ''); + + // Always bind style + style.bind(ctx, this, prevEl); + + if (text) { + + this.setTransform(ctx); + + var textBaseline; + var textAlign = style.textAlign; + var font = style.textFont || style.font; + if (style.textVerticalAlign) { + var rect = textContain.getBoundingRect( + text, font, style.textAlign, 'top' + ); + // Ignore textBaseline + textBaseline = 'middle'; + switch (style.textVerticalAlign) { + case 'middle': + y -= rect.height / 2 - rect.lineHeight / 2; + break; + case 'bottom': + y -= rect.height - rect.lineHeight / 2; + break; + default: + y += rect.lineHeight / 2; + } + } + else { + textBaseline = style.textBaseline; + } + + // TODO Invalid font + ctx.font = font || '12px sans-serif'; + ctx.textAlign = textAlign || 'left'; + // Use canvas default left textAlign. Giving invalid value will cause state not change + if (ctx.textAlign !== textAlign) { + ctx.textAlign = 'left'; + } + ctx.textBaseline = textBaseline || 'alphabetic'; + // Use canvas default alphabetic baseline + if (ctx.textBaseline !== textBaseline) { + ctx.textBaseline = 'alphabetic'; + } + + var lineHeight = textContain.measureText('国', ctx.font).width; + + var textLines = text.split('\n'); + for (var i = 0; i < textLines.length; i++) { + style.hasFill() && ctx.fillText(textLines[i], x, y); + style.hasStroke() && ctx.strokeText(textLines[i], x, y); + y += lineHeight; + } + + this.restoreTransform(ctx); + } + }, + + getBoundingRect: function () { + if (!this._rect) { + var style = this.style; + var textVerticalAlign = style.textVerticalAlign; + var rect = textContain.getBoundingRect( + style.text + '', style.textFont || style.font, style.textAlign, + textVerticalAlign ? 'top' : style.textBaseline + ); + switch (textVerticalAlign) { + case 'middle': + rect.y -= rect.height / 2; + break; + case 'bottom': + rect.y -= rect.height; + break; + } + rect.x += style.x || 0; + rect.y += style.y || 0; + this._rect = rect; + } + return this._rect; + } + }; + + zrUtil.inherits(Text, Displayable); + + module.exports = Text; + + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 圆形 + * @module zrender/shape/Circle + */ + + + + module.exports = __webpack_require__(45).extend({ + + type: 'circle', + + shape: { + cx: 0, + cy: 0, + r: 0 + }, + + + buildPath : function (ctx, shape, inBundle) { + // Better stroking in ShapeBundle + // Always do it may have performence issue ( fill may be 2x more cost) + if (inBundle) { + ctx.moveTo(shape.cx + shape.r, shape.cy); + } + // Better stroking in ShapeBundle + // ctx.moveTo(shape.cx + shape.r, shape.cy); + ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true); + } + }); + + + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 扇形 + * @module zrender/graphic/shape/Sector + */ + + + + module.exports = __webpack_require__(45).extend({ + + type: 'sector', + + shape: { + + cx: 0, + + cy: 0, + + r0: 0, + + r: 0, + + startAngle: 0, + + endAngle: Math.PI * 2, + + clockwise: true + }, + + buildPath: function (ctx, shape) { + + var x = shape.cx; + var y = shape.cy; + var r0 = Math.max(shape.r0 || 0, 0); + var r = Math.max(shape.r, 0); + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + + var unitX = Math.cos(startAngle); + var unitY = Math.sin(startAngle); + + ctx.moveTo(unitX * r0 + x, unitY * r0 + y); + + ctx.lineTo(unitX * r + x, unitY * r + y); + + ctx.arc(x, y, r, startAngle, endAngle, !clockwise); + + ctx.lineTo( + Math.cos(endAngle) * r0 + x, + Math.sin(endAngle) * r0 + y + ); + + if (r0 !== 0) { + ctx.arc(x, y, r0, endAngle, startAngle, clockwise); + } + + ctx.closePath(); + } + }); + + + +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 圆环 + * @module zrender/graphic/shape/Ring + */ + + + module.exports = __webpack_require__(45).extend({ + + type: 'ring', + + shape: { + cx: 0, + cy: 0, + r: 0, + r0: 0 + }, + + buildPath: function (ctx, shape) { + var x = shape.cx; + var y = shape.cy; + var PI2 = Math.PI * 2; + ctx.moveTo(x + shape.r, y); + ctx.arc(x, y, shape.r, 0, PI2, false); + ctx.moveTo(x + shape.r0, y); + ctx.arc(x, y, shape.r0, 0, PI2, true); + } + }); + + + +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 多边形 + * @module zrender/shape/Polygon + */ + + + var polyHelper = __webpack_require__(69); + + module.exports = __webpack_require__(45).extend({ + + type: 'polygon', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + buildPath: function (ctx, shape) { + polyHelper.buildPath(ctx, shape, true); + } + }); + + +/***/ }, +/* 69 */ +/***/ function(module, exports, __webpack_require__) { + + + + var smoothSpline = __webpack_require__(70); + var smoothBezier = __webpack_require__(71); + + module.exports = { + buildPath: function (ctx, shape, closePath) { + var points = shape.points; + var smooth = shape.smooth; + if (points && points.length >= 2) { + if (smooth && smooth !== 'spline') { + var controlPoints = smoothBezier( + points, smooth, closePath, shape.smoothConstraint + ); + + ctx.moveTo(points[0][0], points[0][1]); + var len = points.length; + for (var i = 0; i < (closePath ? len : len - 1); i++) { + var cp1 = controlPoints[i * 2]; + var cp2 = controlPoints[i * 2 + 1]; + var p = points[(i + 1) % len]; + ctx.bezierCurveTo( + cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1] + ); + } + } + else { + if (smooth === 'spline') { + points = smoothSpline(points, closePath); + } + + ctx.moveTo(points[0][0], points[0][1]); + for (var i = 1, l = points.length; i < l; i++) { + ctx.lineTo(points[i][0], points[i][1]); + } + } + + closePath && ctx.closePath(); + } + } + }; + + +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Catmull-Rom spline 插值折线 + * @module zrender/shape/util/smoothSpline + * @author pissang (https://www.github.com/pissang) + * Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + */ + + var vec2 = __webpack_require__(10); + + /** + * @inner + */ + function interpolate(p0, p1, p2, p3, t, t2, t3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + return (2 * (p1 - p2) + v0 + v1) * t3 + + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + + v0 * t + p1; + } + + /** + * @alias module:zrender/shape/util/smoothSpline + * @param {Array} points 线段顶点数组 + * @param {boolean} isLoop + * @return {Array} + */ + module.exports = function (points, isLoop) { + var len = points.length; + var ret = []; + + var distance = 0; + for (var i = 1; i < len; i++) { + distance += vec2.distance(points[i - 1], points[i]); + } + + var segs = distance / 2; + segs = segs < len ? len : segs; + for (var i = 0; i < segs; i++) { + var pos = i / (segs - 1) * (isLoop ? len : len - 1); + var idx = Math.floor(pos); + + var w = pos - idx; + + var p0; + var p1 = points[idx % len]; + var p2; + var p3; + if (!isLoop) { + p0 = points[idx === 0 ? idx : idx - 1]; + p2 = points[idx > len - 2 ? len - 1 : idx + 1]; + p3 = points[idx > len - 3 ? len - 1 : idx + 2]; + } + else { + p0 = points[(idx - 1 + len) % len]; + p2 = points[(idx + 1) % len]; + p3 = points[(idx + 2) % len]; + } + + var w2 = w * w; + var w3 = w * w2; + + ret.push([ + interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), + interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3) + ]); + } + return ret; + }; + + + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 贝塞尔平滑曲线 + * @module zrender/shape/util/smoothBezier + * @author pissang (https://www.github.com/pissang) + * Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + */ + + + var vec2 = __webpack_require__(10); + var v2Min = vec2.min; + var v2Max = vec2.max; + var v2Scale = vec2.scale; + var v2Distance = vec2.distance; + var v2Add = vec2.add; + + /** + * 贝塞尔平滑曲线 + * @alias module:zrender/shape/util/smoothBezier + * @param {Array} points 线段顶点数组 + * @param {number} smooth 平滑等级, 0-1 + * @param {boolean} isLoop + * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内 + * 比如 [[0, 0], [100, 100]], 这个包围盒会与 + * 整个折线的包围盒做一个并集用来约束控制点。 + * @param {Array} 计算出来的控制点数组 + */ + module.exports = function (points, smooth, isLoop, constraint) { + var cps = []; + + var v = []; + var v1 = []; + var v2 = []; + var prevPoint; + var nextPoint; + + var min, max; + if (constraint) { + min = [Infinity, Infinity]; + max = [-Infinity, -Infinity]; + for (var i = 0, len = points.length; i < len; i++) { + v2Min(min, min, points[i]); + v2Max(max, max, points[i]); + } + // 与指定的包围盒做并集 + v2Min(min, min, constraint[0]); + v2Max(max, max, constraint[1]); + } + + for (var i = 0, len = points.length; i < len; i++) { + var point = points[i]; + + if (isLoop) { + prevPoint = points[i ? i - 1 : len - 1]; + nextPoint = points[(i + 1) % len]; + } + else { + if (i === 0 || i === len - 1) { + cps.push(vec2.clone(points[i])); + continue; + } + else { + prevPoint = points[i - 1]; + nextPoint = points[i + 1]; + } + } + + vec2.sub(v, nextPoint, prevPoint); + + // use degree to scale the handle length + v2Scale(v, v, smooth); + + var d0 = v2Distance(point, prevPoint); + var d1 = v2Distance(point, nextPoint); + var sum = d0 + d1; + if (sum !== 0) { + d0 /= sum; + d1 /= sum; + } + + v2Scale(v1, v, -d0); + v2Scale(v2, v, d1); + var cp0 = v2Add([], point, v1); + var cp1 = v2Add([], point, v2); + if (constraint) { + v2Max(cp0, cp0, min); + v2Min(cp0, cp0, max); + v2Max(cp1, cp1, min); + v2Min(cp1, cp1, max); + } + cps.push(cp0); + cps.push(cp1); + } + + if (isLoop) { + cps.push(cps.shift()); + } + + return cps; + }; + + + +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module zrender/graphic/shape/Polyline + */ + + + var polyHelper = __webpack_require__(69); + + module.exports = __webpack_require__(45).extend({ + + type: 'polyline', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + style: { + stroke: '#000', + + fill: null + }, + + buildPath: function (ctx, shape) { + polyHelper.buildPath(ctx, shape, false); + } + }); + + +/***/ }, +/* 73 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 矩形 + * @module zrender/graphic/shape/Rect + */ + + + var roundRectHelper = __webpack_require__(74); + + module.exports = __webpack_require__(45).extend({ + + type: 'rect', + + shape: { + // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4 + // r缩写为1 相当于 [1, 1, 1, 1] + // r缩写为[1] 相当于 [1, 1, 1, 1] + // r缩写为[1, 2] 相当于 [1, 2, 1, 2] + // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2] + r: 0, + + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (ctx, shape) { + var x = shape.x; + var y = shape.y; + var width = shape.width; + var height = shape.height; + if (!shape.r) { + ctx.rect(x, y, width, height); + } + else { + roundRectHelper.buildPath(ctx, shape); + } + ctx.closePath(); + return; + } + }); + + + +/***/ }, +/* 74 */ +/***/ function(module, exports) { + + + + module.exports = { + buildPath: function (ctx, shape) { + var x = shape.x; + var y = shape.y; + var width = shape.width; + var height = shape.height; + var r = shape.r; + var r1; + var r2; + var r3; + var r4; + + // Convert width and height to positive for better borderRadius + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + + if (typeof r === 'number') { + r1 = r2 = r3 = r4 = r; + } + else if (r instanceof Array) { + if (r.length === 1) { + r1 = r2 = r3 = r4 = r[0]; + } + else if (r.length === 2) { + r1 = r3 = r[0]; + r2 = r4 = r[1]; + } + else if (r.length === 3) { + r1 = r[0]; + r2 = r4 = r[1]; + r3 = r[2]; + } + else { + r1 = r[0]; + r2 = r[1]; + r3 = r[2]; + r4 = r[3]; + } + } + else { + r1 = r2 = r3 = r4 = 0; + } + + var total; + if (r1 + r2 > width) { + total = r1 + r2; + r1 *= width / total; + r2 *= width / total; + } + if (r3 + r4 > width) { + total = r3 + r4; + r3 *= width / total; + r4 *= width / total; + } + if (r2 + r3 > height) { + total = r2 + r3; + r2 *= height / total; + r3 *= height / total; + } + if (r1 + r4 > height) { + total = r1 + r4; + r1 *= height / total; + r4 *= height / total; + } + ctx.moveTo(x + r1, y); + ctx.lineTo(x + width - r2, y); + r2 !== 0 && ctx.quadraticCurveTo( + x + width, y, x + width, y + r2 + ); + ctx.lineTo(x + width, y + height - r3); + r3 !== 0 && ctx.quadraticCurveTo( + x + width, y + height, x + width - r3, y + height + ); + ctx.lineTo(x + r4, y + height); + r4 !== 0 && ctx.quadraticCurveTo( + x, y + height, x, y + height - r4 + ); + ctx.lineTo(x, y + r1); + r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y); + } + }; + + +/***/ }, +/* 75 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 直线 + * @module zrender/graphic/shape/Line + */ + + module.exports = __webpack_require__(45).extend({ + + type: 'line', + + shape: { + // Start point + x1: 0, + y1: 0, + // End point + x2: 0, + y2: 0, + + percent: 1 + }, + + style: { + stroke: '#000', + fill: null + }, + + buildPath: function (ctx, shape) { + var x1 = shape.x1; + var y1 = shape.y1; + var x2 = shape.x2; + var y2 = shape.y2; + var percent = shape.percent; + + if (percent === 0) { + return; + } + + ctx.moveTo(x1, y1); + + if (percent < 1) { + x2 = x1 * (1 - percent) + x2 * percent; + y2 = y1 * (1 - percent) + y2 * percent; + } + ctx.lineTo(x2, y2); + }, + + /** + * Get point at percent + * @param {number} percent + * @return {Array.} + */ + pointAt: function (p) { + var shape = this.shape; + return [ + shape.x1 * (1 - p) + shape.x2 * p, + shape.y1 * (1 - p) + shape.y2 * p + ]; + } + }); + + + +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 贝塞尔曲线 + * @module zrender/shape/BezierCurve + */ + + + var curveTool = __webpack_require__(50); + var vec2 = __webpack_require__(10); + var quadraticSubdivide = curveTool.quadraticSubdivide; + var cubicSubdivide = curveTool.cubicSubdivide; + var quadraticAt = curveTool.quadraticAt; + var cubicAt = curveTool.cubicAt; + var quadraticDerivativeAt = curveTool.quadraticDerivativeAt; + var cubicDerivativeAt = curveTool.cubicDerivativeAt; + + var out = []; + + function someVectorAt(shape, t, isTangent) { + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + if (cpx2 === null || cpy2 === null) { + return [ + (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t), + (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t) + ]; + } + else { + return [ + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t), + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t) + ]; + } + } + module.exports = __webpack_require__(45).extend({ + + type: 'bezier-curve', + + shape: { + x1: 0, + y1: 0, + x2: 0, + y2: 0, + cpx1: 0, + cpy1: 0, + // cpx2: 0, + // cpy2: 0 + + // Curve show percent, for animating + percent: 1 + }, + + style: { + stroke: '#000', + fill: null + }, + + buildPath: function (ctx, shape) { + var x1 = shape.x1; + var y1 = shape.y1; + var x2 = shape.x2; + var y2 = shape.y2; + var cpx1 = shape.cpx1; + var cpy1 = shape.cpy1; + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + var percent = shape.percent; + if (percent === 0) { + return; + } + + ctx.moveTo(x1, y1); + + if (cpx2 == null || cpy2 == null) { + if (percent < 1) { + quadraticSubdivide( + x1, cpx1, x2, percent, out + ); + cpx1 = out[1]; + x2 = out[2]; + quadraticSubdivide( + y1, cpy1, y2, percent, out + ); + cpy1 = out[1]; + y2 = out[2]; + } + + ctx.quadraticCurveTo( + cpx1, cpy1, + x2, y2 + ); + } + else { + if (percent < 1) { + cubicSubdivide( + x1, cpx1, cpx2, x2, percent, out + ); + cpx1 = out[1]; + cpx2 = out[2]; + x2 = out[3]; + cubicSubdivide( + y1, cpy1, cpy2, y2, percent, out + ); + cpy1 = out[1]; + cpy2 = out[2]; + y2 = out[3]; + } + ctx.bezierCurveTo( + cpx1, cpy1, + cpx2, cpy2, + x2, y2 + ); + } + }, + + /** + * Get point at percent + * @param {number} t + * @return {Array.} + */ + pointAt: function (t) { + return someVectorAt(this.shape, t, false); + }, + + /** + * Get tangent at percent + * @param {number} t + * @return {Array.} + */ + tangentAt: function (t) { + var p = someVectorAt(this.shape, t, true); + return vec2.normalize(p, p); + } + }); + + + +/***/ }, +/* 77 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 圆弧 + * @module zrender/graphic/shape/Arc + */ + + + module.exports = __webpack_require__(45).extend({ + + type: 'arc', + + shape: { + + cx: 0, + + cy: 0, + + r: 0, + + startAngle: 0, + + endAngle: Math.PI * 2, + + clockwise: true + }, + + style: { + + stroke: '#000', + + fill: null + }, + + buildPath: function (ctx, shape) { + + var x = shape.cx; + var y = shape.cy; + var r = Math.max(shape.r, 0); + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + + var unitX = Math.cos(startAngle); + var unitY = Math.sin(startAngle); + + ctx.moveTo(unitX * r + x, unitY * r + y); + ctx.arc(x, y, r, startAngle, endAngle, !clockwise); + } + }); + + +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { + + // CompoundPath to improve performance + + + var Path = __webpack_require__(45); + module.exports = Path.extend({ + + type: 'compound', + + shape: { + + paths: null + }, + + _updatePathDirty: function () { + var dirtyPath = this.__dirtyPath; + var paths = this.shape.paths; + for (var i = 0; i < paths.length; i++) { + // Mark as dirty if any subpath is dirty + dirtyPath = dirtyPath || paths[i].__dirtyPath; + } + this.__dirtyPath = dirtyPath; + this.__dirty = this.__dirty || dirtyPath; + }, + + beforeBrush: function () { + this._updatePathDirty(); + var paths = this.shape.paths || []; + var scale = this.getGlobalScale(); + // Update path scale + for (var i = 0; i < paths.length; i++) { + paths[i].path.setScale(scale[0], scale[1]); + } + }, + + buildPath: function (ctx, shape) { + var paths = shape.paths || []; + for (var i = 0; i < paths.length; i++) { + paths[i].buildPath(ctx, paths[i].shape, true); + } + }, + + afterBrush: function () { + var paths = this.shape.paths; + for (var i = 0; i < paths.length; i++) { + paths[i].__dirtyPath = false; + } + }, + + getBoundingRect: function () { + this._updatePathDirty(); + return Path.prototype.getBoundingRect.call(this); + } + }); + + +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var Gradient = __webpack_require__(61); + + /** + * x, y, x2, y2 are all percent from 0 to 1 + * @param {number} [x=0] + * @param {number} [y=0] + * @param {number} [x2=1] + * @param {number} [y2=0] + * @param {Array.} colorStops + * @param {boolean} [globalCoord=false] + */ + var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) { + this.x = x == null ? 0 : x; + + this.y = y == null ? 0 : y; + + this.x2 = x2 == null ? 1 : x2; + + this.y2 = y2 == null ? 0 : y2; + + // Can be cloned + this.type = 'linear'; + + // If use global coord + this.global = globalCoord || false; + + Gradient.call(this, colorStops); + }; + + LinearGradient.prototype = { + + constructor: LinearGradient + }; + + zrUtil.inherits(LinearGradient, Gradient); + + module.exports = LinearGradient; + + +/***/ }, +/* 80 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var Gradient = __webpack_require__(61); + + /** + * x, y, r are all percent from 0 to 1 + * @param {number} [x=0.5] + * @param {number} [y=0.5] + * @param {number} [r=0.5] + * @param {Array.} [colorStops] + * @param {boolean} [globalCoord=false] + */ + var RadialGradient = function (x, y, r, colorStops, globalCoord) { + this.x = x == null ? 0.5 : x; + + this.y = y == null ? 0.5 : y; + + this.r = r == null ? 0.5 : r; + + // Can be cloned + this.type = 'radial'; + + // If use global coord + this.global = globalCoord || false; + + Gradient.call(this, colorStops); + }; + + RadialGradient.prototype = { + + constructor: RadialGradient + }; + + zrUtil.inherits(RadialGradient, Gradient); + + module.exports = RadialGradient; + + +/***/ }, +/* 81 */ +/***/ function(module, exports, __webpack_require__) { + + /*! + * ZRender, a high performance 2d drawing library. + * + * Copyright (c) 2013, Baidu Inc. + * All rights reserved. + * + * LICENSE + * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt + */ + // Global defines + + var guid = __webpack_require__(32); + var env = __webpack_require__(2); + + var Handler = __webpack_require__(82); + var Storage = __webpack_require__(84); + var Animation = __webpack_require__(86); + var HandlerProxy = __webpack_require__(89); + + var useVML = !env.canvasSupported; + + var painterCtors = { + canvas: __webpack_require__(91) + }; + + var instances = {}; // ZRender实例map索引 + + var zrender = {}; + /** + * @type {string} + */ + zrender.version = '3.1.3'; + + /** + * Initializing a zrender instance + * @param {HTMLElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + * @return {module:zrender/ZRender} + */ + zrender.init = function(dom, opts) { + var zr = new ZRender(guid(), dom, opts); + instances[zr.id] = zr; + return zr; + }; + + /** + * Dispose zrender instance + * @param {module:zrender/ZRender} zr + */ + zrender.dispose = function (zr) { + if (zr) { + zr.dispose(); + } + else { + for (var key in instances) { + instances[key].dispose(); + } + instances = {}; + } + + return zrender; + }; + + /** + * Get zrender instance by id + * @param {string} id zrender instance id + * @return {module:zrender/ZRender} + */ + zrender.getInstance = function (id) { + return instances[id]; + }; + + zrender.registerPainter = function (name, Ctor) { + painterCtors[name] = Ctor; + }; + + function delInstance(id) { + delete instances[id]; + } + + /** + * @module zrender/ZRender + */ + /** + * @constructor + * @alias module:zrender/ZRender + * @param {string} id + * @param {HTMLDomElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + */ + var ZRender = function(id, dom, opts) { + + opts = opts || {}; + + /** + * @type {HTMLDomElement} + */ + this.dom = dom; + + /** + * @type {string} + */ + this.id = id; + + var self = this; + var storage = new Storage(); + + var rendererType = opts.renderer; + if (useVML) { + if (!painterCtors.vml) { + throw new Error('You need to require \'zrender/vml/vml\' to support IE8'); + } + rendererType = 'vml'; + } + else if (!rendererType || !painterCtors[rendererType]) { + rendererType = 'canvas'; + } + var painter = new painterCtors[rendererType](dom, storage, opts); + + this.storage = storage; + this.painter = painter; + + var handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null; + this.handler = new Handler(storage, painter, handerProxy); + + /** + * @type {module:zrender/animation/Animation} + */ + this.animation = new Animation({ + stage: { + update: function () { + if (self._needsRefresh) { + self.refreshImmediately(); + } + if (self._needsRefreshHover) { + self.refreshHoverImmediately(); + } + } + } + }); + this.animation.start(); + + /** + * @type {boolean} + * @private + */ + this._needsRefresh; + + // 修改 storage.delFromMap, 每次删除元素之前删除动画 + // FIXME 有点ugly + var oldDelFromMap = storage.delFromMap; + var oldAddToMap = storage.addToMap; + + storage.delFromMap = function (elId) { + var el = storage.get(elId); + + oldDelFromMap.call(storage, elId); + + el && el.removeSelfFromZr(self); + }; + + storage.addToMap = function (el) { + oldAddToMap.call(storage, el); + + el.addSelfToZr(self); + }; + }; + + ZRender.prototype = { + + constructor: ZRender, + /** + * 获取实例唯一标识 + * @return {string} + */ + getId: function () { + return this.id; + }, + + /** + * 添加元素 + * @param {module:zrender/Element} el + */ + add: function (el) { + this.storage.addRoot(el); + this._needsRefresh = true; + }, + + /** + * 删除元素 + * @param {module:zrender/Element} el + */ + remove: function (el) { + this.storage.delRoot(el); + this._needsRefresh = true; + }, + + /** + * Change configuration of layer + * @param {string} zLevel + * @param {Object} config + * @param {string} [config.clearColor=0] Clear color + * @param {string} [config.motionBlur=false] If enable motion blur + * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer + */ + configLayer: function (zLevel, config) { + this.painter.configLayer(zLevel, config); + this._needsRefresh = true; + }, + + /** + * Repaint the canvas immediately + */ + refreshImmediately: function () { + // Clear needsRefresh ahead to avoid something wrong happens in refresh + // Or it will cause zrender refreshes again and again. + this._needsRefresh = false; + this.painter.refresh(); + /** + * Avoid trigger zr.refresh in Element#beforeUpdate hook + */ + this._needsRefresh = false; + }, + + /** + * Mark and repaint the canvas in the next frame of browser + */ + refresh: function() { + this._needsRefresh = true; + }, + + /** + * Add element to hover layer + * @param {module:zrender/Element} el + * @param {Object} style + */ + addHover: function (el, style) { + if (this.painter.addHover) { + this.painter.addHover(el, style); + this.refreshHover(); + } + }, + + /** + * Add element from hover layer + * @param {module:zrender/Element} el + */ + removeHover: function (el) { + if (this.painter.removeHover) { + this.painter.removeHover(el); + this.refreshHover(); + } + }, + + /** + * Clear all hover elements in hover layer + * @param {module:zrender/Element} el + */ + clearHover: function () { + if (this.painter.clearHover) { + this.painter.clearHover(); + this.refreshHover(); + } + }, + + /** + * Refresh hover in next frame + */ + refreshHover: function () { + this._needsRefreshHover = true; + }, + + /** + * Refresh hover immediately + */ + refreshHoverImmediately: function () { + this._needsRefreshHover = false; + this.painter.refreshHover && this.painter.refreshHover(); + }, + + /** + * Resize the canvas. + * Should be invoked when container size is changed + */ + resize: function() { + this.painter.resize(); + this.handler.resize(); + }, + + /** + * Stop and clear all animation immediately + */ + clearAnimation: function () { + this.animation.clear(); + }, + + /** + * Get container width + */ + getWidth: function() { + return this.painter.getWidth(); + }, + + /** + * Get container height + */ + getHeight: function() { + return this.painter.getHeight(); + }, + + /** + * Export the canvas as Base64 URL + * @param {string} type + * @param {string} [backgroundColor='#fff'] + * @return {string} Base64 URL + */ + // toDataURL: function(type, backgroundColor) { + // return this.painter.getRenderedCanvas({ + // backgroundColor: backgroundColor + // }).toDataURL(type); + // }, + + /** + * Converting a path to image. + * It has much better performance of drawing image rather than drawing a vector path. + * @param {module:zrender/graphic/Path} e + * @param {number} width + * @param {number} height + */ + pathToImage: function(e, width, height) { + var id = guid(); + return this.painter.pathToImage(id, e, width, height); + }, + + /** + * Set default cursor + * @param {string} [cursorStyle='default'] 例如 crosshair + */ + setCursorStyle: function (cursorStyle) { + this.handler.setCursorStyle(cursorStyle); + }, + + /** + * Bind event + * + * @param {string} eventName Event name + * @param {Function} eventHandler Handler function + * @param {Object} [context] Context object + */ + on: function(eventName, eventHandler, context) { + this.handler.on(eventName, eventHandler, context); + }, + + /** + * Unbind event + * @param {string} eventName Event name + * @param {Function} [eventHandler] Handler function + */ + off: function(eventName, eventHandler) { + this.handler.off(eventName, eventHandler); + }, + + /** + * Trigger event manually + * + * @param {string} eventName Event name + * @param {event=} event Event object + */ + trigger: function (eventName, event) { + this.handler.trigger(eventName, event); + }, + + + /** + * Clear all objects and the canvas. + */ + clear: function () { + this.storage.delRoot(); + this.painter.clear(); + }, + + /** + * Dispose self. + */ + dispose: function () { + this.animation.stop(); + + this.clear(); + this.storage.dispose(); + this.painter.dispose(); + this.handler.dispose(); + + this.animation = + this.storage = + this.painter = + this.handler = null; + + delInstance(this.id); + } + }; + + module.exports = zrender; + + + +/***/ }, +/* 82 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Handler + * @module zrender/Handler + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + * pissang (shenyi.914@gmail.com) + */ + + + var util = __webpack_require__(4); + var Draggable = __webpack_require__(83); + + var Eventful = __webpack_require__(33); + + function makeEventPacket(eveType, target, event) { + return { + type: eveType, + event: event, + target: target, + cancelBubble: false, + offsetX: event.zrX, + offsetY: event.zrY, + gestureEvent: event.gestureEvent, + pinchX: event.pinchX, + pinchY: event.pinchY, + pinchScale: event.pinchScale, + wheelDelta: event.zrDelta + }; + } + + function EmptyProxy () {} + EmptyProxy.prototype.dispose = function () {}; + + var handlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove' + ]; + /** + * @alias module:zrender/Handler + * @constructor + * @extends module:zrender/mixin/Eventful + * @param {HTMLElement} root Main HTML element for painting. + * @param {module:zrender/Storage} storage Storage instance. + * @param {module:zrender/Painter} painter Painter instance. + */ + var Handler = function(storage, painter, proxy) { + Eventful.call(this); + + this.storage = storage; + + this.painter = painter; + + proxy = proxy || new EmptyProxy(); + /** + * Proxy of event. can be Dom, WebGLSurface, etc. + */ + this.proxy = proxy; + + // Attach handler + proxy.handler = this; + + /** + * @private + * @type {boolean} + */ + this._hovered; + + /** + * @private + * @type {Date} + */ + this._lastTouchMoment; + + /** + * @private + * @type {number} + */ + this._lastX; + + /** + * @private + * @type {number} + */ + this._lastY; + + + Draggable.call(this); + + util.each(handlerNames, function (name) { + proxy.on && proxy.on(name, this[name], this); + }, this); + }; + + Handler.prototype = { + + constructor: Handler, + + mousemove: function (event) { + var x = event.zrX; + var y = event.zrY; + + var hovered = this.findHover(x, y, null); + var lastHovered = this._hovered; + var proxy = this.proxy; + + this._hovered = hovered; + + proxy.setCursor && proxy.setCursor(hovered ? hovered.cursor : 'default'); + + // Mouse out on previous hovered element + if (lastHovered && hovered !== lastHovered && lastHovered.__zr) { + this.dispatchToElement(lastHovered, 'mouseout', event); + } + + // Mouse moving on one element + this.dispatchToElement(hovered, 'mousemove', event); + + // Mouse over on a new element + if (hovered && hovered !== lastHovered) { + this.dispatchToElement(hovered, 'mouseover', event); + } + }, + + mouseout: function (event) { + this.dispatchToElement(this._hovered, 'mouseout', event); + + this.trigger('globalout', { + event: event + }); + }, + + /** + * Resize + */ + resize: function (event) { + this._hovered = null; + }, + + /** + * Dispatch event + * @param {string} eventName + * @param {event=} eventArgs + */ + dispatch: function (eventName, eventArgs) { + var handler = this[eventName]; + handler && handler.call(this, eventArgs); + }, + + /** + * Dispose + */ + dispose: function () { + + this.proxy.dispose(); + + this.storage = + this.proxy = + this.painter = null; + }, + + /** + * 设置默认的cursor style + * @param {string} [cursorStyle='default'] 例如 crosshair + */ + setCursorStyle: function (cursorStyle) { + var proxy = this.proxy; + proxy.setCursor && proxy.setCursor(cursorStyle); + }, + + /** + * 事件分发代理 + * + * @private + * @param {Object} targetEl 目标图形元素 + * @param {string} eventName 事件名称 + * @param {Object} event 事件对象 + */ + dispatchToElement: function (targetEl, eventName, event) { + var eventHandler = 'on' + eventName; + var eventPacket = makeEventPacket(eventName, targetEl, event); + + var el = targetEl; + + while (el) { + el[eventHandler] + && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket)); + + el.trigger(eventName, eventPacket); + + el = el.parent; + + if (eventPacket.cancelBubble) { + break; + } + } + + if (!eventPacket.cancelBubble) { + // 冒泡到顶级 zrender 对象 + this.trigger(eventName, eventPacket); + // 分发事件到用户自定义层 + // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在 + this.painter && this.painter.eachOtherLayer(function (layer) { + if (typeof(layer[eventHandler]) == 'function') { + layer[eventHandler].call(layer, eventPacket); + } + if (layer.trigger) { + layer.trigger(eventName, eventPacket); + } + }); + } + }, + + /** + * @private + * @param {number} x + * @param {number} y + * @param {module:zrender/graphic/Displayable} exclude + * @method + */ + findHover: function(x, y, exclude) { + var list = this.storage.getDisplayList(); + for (var i = list.length - 1; i >= 0 ; i--) { + if (!list[i].silent + && list[i] !== exclude + // getDisplayList may include ignored item in VML mode + && !list[i].ignore + && isHover(list[i], x, y)) { + return list[i]; + } + } + } + }; + + // Common handlers + util.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick'], function (name) { + Handler.prototype[name] = function (event) { + // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover + var hovered = this.findHover(event.zrX, event.zrY, null); + + if (name === 'mousedown') { + this._downel = hovered; + // In case click triggered before mouseup + this._upel = hovered; + } + else if (name === 'mosueup') { + this._upel = hovered; + } + else if (name === 'click') { + if (this._downel !== this._upel) { + return; + } + } + + this.dispatchToElement(hovered, name, event); + }; + }); + + function isHover(displayable, x, y) { + if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) { + var el = displayable; + while (el) { + // If ancestor is silent or clipped by ancestor + if (el.silent || (el.clipPath && !el.clipPath.contain(x, y))) { + return false; + } + el = el.parent; + } + return true; + } + + return false; + } + + util.mixin(Handler, Eventful); + util.mixin(Handler, Draggable); + + module.exports = Handler; + + +/***/ }, +/* 83 */ +/***/ function(module, exports) { + + // TODO Draggable for group + // FIXME Draggable on element which has parent rotation or scale + + function Draggable() { + + this.on('mousedown', this._dragStart, this); + this.on('mousemove', this._drag, this); + this.on('mouseup', this._dragEnd, this); + this.on('globalout', this._dragEnd, this); + // this._dropTarget = null; + // this._draggingTarget = null; + + // this._x = 0; + // this._y = 0; + } + + Draggable.prototype = { + + constructor: Draggable, + + _dragStart: function (e) { + var draggingTarget = e.target; + if (draggingTarget && draggingTarget.draggable) { + this._draggingTarget = draggingTarget; + draggingTarget.dragging = true; + this._x = e.offsetX; + this._y = e.offsetY; + + this.dispatchToElement(draggingTarget, 'dragstart', e.event); + } + }, + + _drag: function (e) { + var draggingTarget = this._draggingTarget; + if (draggingTarget) { + + var x = e.offsetX; + var y = e.offsetY; + + var dx = x - this._x; + var dy = y - this._y; + this._x = x; + this._y = y; + + draggingTarget.drift(dx, dy, e); + this.dispatchToElement(draggingTarget, 'drag', e.event); + + var dropTarget = this.findHover(x, y, draggingTarget); + var lastDropTarget = this._dropTarget; + this._dropTarget = dropTarget; + + if (draggingTarget !== dropTarget) { + if (lastDropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(lastDropTarget, 'dragleave', e.event); + } + if (dropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(dropTarget, 'dragenter', e.event); + } + } + } + }, + + _dragEnd: function (e) { + var draggingTarget = this._draggingTarget; + + if (draggingTarget) { + draggingTarget.dragging = false; + } + + this.dispatchToElement(draggingTarget, 'dragend', e.event); + + if (this._dropTarget) { + this.dispatchToElement(this._dropTarget, 'drop', e.event); + } + + this._draggingTarget = null; + this._dropTarget = null; + } + + }; + + module.exports = Draggable; + + +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Storage内容仓库模块 + * @module zrender/Storage + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * @author errorrik (errorrik@gmail.com) + * @author pissang (https://github.com/pissang/) + */ + + + var util = __webpack_require__(4); + var env = __webpack_require__(2); + + var Group = __webpack_require__(30); + + // Use timsort because in most case elements are partially sorted + // https://jsfiddle.net/pissang/jr4x7mdm/8/ + var timsort = __webpack_require__(85); + + function shapeCompareFunc(a, b) { + if (a.zlevel === b.zlevel) { + if (a.z === b.z) { + // if (a.z2 === b.z2) { + // // FIXME Slow has renderidx compare + // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement + // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012 + // return a.__renderidx - b.__renderidx; + // } + return a.z2 - b.z2; + } + return a.z - b.z; + } + return a.zlevel - b.zlevel; + } + /** + * 内容仓库 (M) + * @alias module:zrender/Storage + * @constructor + */ + var Storage = function () { + // 所有常规形状,id索引的map + this._elements = {}; + + this._roots = []; + + this._displayList = []; + + this._displayListLen = 0; + }; + + Storage.prototype = { + + constructor: Storage, + + /** + * @param {Function} cb + * + */ + traverse: function (cb, context) { + for (var i = 0; i < this._roots.length; i++) { + this._roots[i].traverse(cb, context); + } + }, + + /** + * 返回所有图形的绘制队列 + * @param {boolean} [update=false] 是否在返回前更新该数组 + * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效 + * + * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList} + * @return {Array.} + */ + getDisplayList: function (update, includeIgnore) { + includeIgnore = includeIgnore || false; + if (update) { + this.updateDisplayList(includeIgnore); + } + return this._displayList; + }, + + /** + * 更新图形的绘制队列。 + * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中, + * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列 + * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组 + */ + updateDisplayList: function (includeIgnore) { + this._displayListLen = 0; + var roots = this._roots; + var displayList = this._displayList; + for (var i = 0, len = roots.length; i < len; i++) { + this._updateAndAddDisplayable(roots[i], null, includeIgnore); + } + displayList.length = this._displayListLen; + + // for (var i = 0, len = displayList.length; i < len; i++) { + // displayList[i].__renderidx = i; + // } + + // displayList.sort(shapeCompareFunc); + env.canvasSupported && timsort(displayList, shapeCompareFunc); + }, + + _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) { + + if (el.ignore && !includeIgnore) { + return; + } + + el.beforeUpdate(); + + if (el.__dirty) { + + el.update(); + + } + + el.afterUpdate(); + + var clipPath = el.clipPath; + if (clipPath) { + // clipPath 的变换是基于 group 的变换 + clipPath.parent = el; + clipPath.updateTransform(); + + // FIXME 效率影响 + if (clipPaths) { + clipPaths = clipPaths.slice(); + clipPaths.push(clipPath); + } + else { + clipPaths = [clipPath]; + } + } + + if (el.isGroup) { + var children = el._children; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + + // Force to mark as dirty if group is dirty + // FIXME __dirtyPath ? + if (el.__dirty) { + child.__dirty = true; + } + + this._updateAndAddDisplayable(child, clipPaths, includeIgnore); + } + + // Mark group clean here + el.__dirty = false; + + } + else { + el.__clipPaths = clipPaths; + + this._displayList[this._displayListLen++] = el; + } + }, + + /** + * 添加图形(Shape)或者组(Group)到根节点 + * @param {module:zrender/Element} el + */ + addRoot: function (el) { + // Element has been added + if (this._elements[el.id]) { + return; + } + + if (el instanceof Group) { + el.addChildrenToStorage(this); + } + + this.addToMap(el); + this._roots.push(el); + }, + + /** + * 删除指定的图形(Shape)或者组(Group) + * @param {string|Array.} [elId] 如果为空清空整个Storage + */ + delRoot: function (elId) { + if (elId == null) { + // 不指定elId清空 + for (var i = 0; i < this._roots.length; i++) { + var root = this._roots[i]; + if (root instanceof Group) { + root.delChildrenFromStorage(this); + } + } + + this._elements = {}; + this._roots = []; + this._displayList = []; + this._displayListLen = 0; + + return; + } + + if (elId instanceof Array) { + for (var i = 0, l = elId.length; i < l; i++) { + this.delRoot(elId[i]); + } + return; + } + + var el; + if (typeof(elId) == 'string') { + el = this._elements[elId]; + } + else { + el = elId; + } + + var idx = util.indexOf(this._roots, el); + if (idx >= 0) { + this.delFromMap(el.id); + this._roots.splice(idx, 1); + if (el instanceof Group) { + el.delChildrenFromStorage(this); + } + } + }, + + addToMap: function (el) { + if (el instanceof Group) { + el.__storage = this; + } + el.dirty(false); + + this._elements[el.id] = el; + + return this; + }, + + get: function (elId) { + return this._elements[elId]; + }, + + delFromMap: function (elId) { + var elements = this._elements; + var el = elements[elId]; + if (el) { + delete elements[elId]; + if (el instanceof Group) { + el.__storage = null; + } + } + + return this; + }, + + /** + * 清空并且释放Storage + */ + dispose: function () { + this._elements = + this._renderList = + this._roots = null; + }, + + displayableSortFunc: shapeCompareFunc + }; + + module.exports = Storage; + + + +/***/ }, +/* 85 */ +/***/ function(module, exports) { + + // https://github.com/mziccard/node-timsort + + var DEFAULT_MIN_MERGE = 32; + + var DEFAULT_MIN_GALLOPING = 7; + + var DEFAULT_TMP_STORAGE_LENGTH = 256; + + function minRunLength(n) { + var r = 0; + + while (n >= DEFAULT_MIN_MERGE) { + r |= n & 1; + n >>= 1; + } + + return n + r; + } + + function makeAscendingRun(array, lo, hi, compare) { + var runHi = lo + 1; + + if (runHi === hi) { + return 1; + } + + if (compare(array[runHi++], array[lo]) < 0) { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) { + runHi++; + } + + reverseRun(array, lo, runHi); + } + else { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { + runHi++; + } + } + + return runHi - lo; + } + + function reverseRun(array, lo, hi) { + hi--; + + while (lo < hi) { + var t = array[lo]; + array[lo++] = array[hi]; + array[hi--] = t; + } + } + + function binaryInsertionSort(array, lo, hi, start, compare) { + if (start === lo) { + start++; + } + + for (; start < hi; start++) { + var pivot = array[start]; + + var left = lo; + var right = start; + var mid; + + while (left < right) { + mid = left + right >>> 1; + + if (compare(pivot, array[mid]) < 0) { + right = mid; + } + else { + left = mid + 1; + } + } + + var n = start - left; + + switch (n) { + case 3: + array[left + 3] = array[left + 2]; + + case 2: + array[left + 2] = array[left + 1]; + + case 1: + array[left + 1] = array[left]; + break; + default: + while (n > 0) { + array[left + n] = array[left + n - 1]; + n--; + } + } + + array[left] = pivot; + } + } + + function gallopLeft(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + + if (compare(value, array[start + hint]) > 0) { + maxOffset = length - hint; + + while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + lastOffset += hint; + offset += hint; + } + else { + maxOffset = hint + 1; + while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + + lastOffset++; + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + + if (compare(value, array[start + m]) > 0) { + lastOffset = m + 1; + } + else { + offset = m; + } + } + return offset; + } + + function gallopRight(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + + if (compare(value, array[start + hint]) < 0) { + maxOffset = hint + 1; + + while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + else { + maxOffset = length - hint; + + while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + lastOffset += hint; + offset += hint; + } + + lastOffset++; + + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + + if (compare(value, array[start + m]) < 0) { + offset = m; + } + else { + lastOffset = m + 1; + } + } + + return offset; + } + + function TimSort(array, compare) { + var minGallop = DEFAULT_MIN_GALLOPING; + var length = 0; + var tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH; + var stackLength = 0; + var runStart; + var runLength; + var stackSize = 0; + + length = array.length; + + if (length < 2 * DEFAULT_TMP_STORAGE_LENGTH) { + tmpStorageLength = length >>> 1; + } + + var tmp = []; + + stackLength = length < 120 ? 5 : length < 1542 ? 10 : length < 119151 ? 19 : 40; + + runStart = []; + runLength = []; + + function pushRun(_runStart, _runLength) { + runStart[stackSize] = _runStart; + runLength[stackSize] = _runLength; + stackSize += 1; + } + + function mergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + + if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) { + if (runLength[n - 1] < runLength[n + 1]) { + n--; + } + } + else if (runLength[n] > runLength[n + 1]) { + break; + } + mergeAt(n); + } + } + + function forceMergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + + if (n > 0 && runLength[n - 1] < runLength[n + 1]) { + n--; + } + + mergeAt(n); + } + } + + function mergeAt(i) { + var start1 = runStart[i]; + var length1 = runLength[i]; + var start2 = runStart[i + 1]; + var length2 = runLength[i + 1]; + + runLength[i] = length1 + length2; + + if (i === stackSize - 3) { + runStart[i + 1] = runStart[i + 2]; + runLength[i + 1] = runLength[i + 2]; + } + + stackSize--; + + var k = gallopRight(array[start2], array, start1, length1, 0, compare); + start1 += k; + length1 -= k; + + if (length1 === 0) { + return; + } + + length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); + + if (length2 === 0) { + return; + } + + if (length1 <= length2) { + mergeLow(start1, length1, start2, length2); + } + else { + mergeHigh(start1, length1, start2, length2); + } + } + + function mergeLow(start1, length1, start2, length2) { + var i = 0; + + for (i = 0; i < length1; i++) { + tmp[i] = array[start1 + i]; + } + + var cursor1 = 0; + var cursor2 = start2; + var dest = start1; + + array[dest++] = array[cursor2++]; + + if (--length2 === 0) { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + return; + } + + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + return; + } + + var _minGallop = minGallop; + var count1, count2, exit; + + while (1) { + count1 = 0; + count2 = 0; + exit = false; + + do { + if (compare(array[cursor2], tmp[cursor1]) < 0) { + array[dest++] = array[cursor2++]; + count2++; + count1 = 0; + + if (--length2 === 0) { + exit = true; + break; + } + } + else { + array[dest++] = tmp[cursor1++]; + count1++; + count2 = 0; + if (--length1 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + + if (exit) { + break; + } + + do { + count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); + + if (count1 !== 0) { + for (i = 0; i < count1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + + dest += count1; + cursor1 += count1; + length1 -= count1; + if (length1 <= 1) { + exit = true; + break; + } + } + + array[dest++] = array[cursor2++]; + + if (--length2 === 0) { + exit = true; + break; + } + + count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); + + if (count2 !== 0) { + for (i = 0; i < count2; i++) { + array[dest + i] = array[cursor2 + i]; + } + + dest += count2; + cursor2 += count2; + length2 -= count2; + + if (length2 === 0) { + exit = true; + break; + } + } + array[dest++] = tmp[cursor1++]; + + if (--length1 === 1) { + exit = true; + break; + } + + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + + if (exit) { + break; + } + + if (_minGallop < 0) { + _minGallop = 0; + } + + _minGallop += 2; + } + + minGallop = _minGallop; + + minGallop < 1 && (minGallop = 1); + + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + } + else if (length1 === 0) { + throw new Error(); + // throw new Error('mergeLow preconditions were not respected'); + } + else { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + } + } + + function mergeHigh (start1, length1, start2, length2) { + var i = 0; + + for (i = 0; i < length2; i++) { + tmp[i] = array[start2 + i]; + } + + var cursor1 = start1 + length1 - 1; + var cursor2 = length2 - 1; + var dest = start2 + length2 - 1; + var customCursor = 0; + var customDest = 0; + + array[dest--] = array[cursor1--]; + + if (--length1 === 0) { + customCursor = dest - (length2 - 1); + + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + + return; + } + + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + array[dest] = tmp[cursor2]; + return; + } + + var _minGallop = minGallop; + + while (true) { + var count1 = 0; + var count2 = 0; + var exit = false; + + do { + if (compare(tmp[cursor2], array[cursor1]) < 0) { + array[dest--] = array[cursor1--]; + count1++; + count2 = 0; + if (--length1 === 0) { + exit = true; + break; + } + } + else { + array[dest--] = tmp[cursor2--]; + count2++; + count1 = 0; + if (--length2 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + + if (exit) { + break; + } + + do { + count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare); + + if (count1 !== 0) { + dest -= count1; + cursor1 -= count1; + length1 -= count1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = count1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + if (length1 === 0) { + exit = true; + break; + } + } + + array[dest--] = tmp[cursor2--]; + + if (--length2 === 1) { + exit = true; + break; + } + + count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare); + + if (count2 !== 0) { + dest -= count2; + cursor2 -= count2; + length2 -= count2; + customDest = dest + 1; + customCursor = cursor2 + 1; + + for (i = 0; i < count2; i++) { + array[customDest + i] = tmp[customCursor + i]; + } + + if (length2 <= 1) { + exit = true; + break; + } + } + + array[dest--] = array[cursor1--]; + + if (--length1 === 0) { + exit = true; + break; + } + + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + + if (exit) { + break; + } + + if (_minGallop < 0) { + _minGallop = 0; + } + + _minGallop += 2; + } + + minGallop = _minGallop; + + if (minGallop < 1) { + minGallop = 1; + } + + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + array[dest] = tmp[cursor2]; + } + else if (length2 === 0) { + throw new Error(); + // throw new Error('mergeHigh preconditions were not respected'); + } + else { + customCursor = dest - (length2 - 1); + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + } + } + + this.mergeRuns = mergeRuns; + this.forceMergeRuns = forceMergeRuns; + this.pushRun = pushRun; + } + + function sort(array, compare, lo, hi) { + if (!lo) { + lo = 0; + } + if (!hi) { + hi = array.length; + } + + var remaining = hi - lo; + + if (remaining < 2) { + return; + } + + var runLength = 0; + + if (remaining < DEFAULT_MIN_MERGE) { + runLength = makeAscendingRun(array, lo, hi, compare); + binaryInsertionSort(array, lo, hi, lo + runLength, compare); + return; + } + + var ts = new TimSort(array, compare); + + var minRun = minRunLength(remaining); + + do { + runLength = makeAscendingRun(array, lo, hi, compare); + if (runLength < minRun) { + var force = remaining; + if (force > minRun) { + force = minRun; + } + + binaryInsertionSort(array, lo, lo + force, lo + runLength, compare); + runLength = force; + } + + ts.pushRun(lo, runLength); + ts.mergeRuns(); + + remaining -= runLength; + lo += runLength; + } while (remaining !== 0); + + ts.forceMergeRuns(); + } + + module.exports = sort; + + +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 动画主类, 调度和管理所有动画控制器 + * + * @module zrender/animation/Animation + * @author pissang(https://github.com/pissang) + */ + // TODO Additive animation + // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/ + // https://developer.apple.com/videos/wwdc2014/#236 + + + var util = __webpack_require__(4); + var Dispatcher = __webpack_require__(87).Dispatcher; + + var requestAnimationFrame = __webpack_require__(88); + + var Animator = __webpack_require__(36); + /** + * @typedef {Object} IZRenderStage + * @property {Function} update + */ + + /** + * @alias module:zrender/animation/Animation + * @constructor + * @param {Object} [options] + * @param {Function} [options.onframe] + * @param {IZRenderStage} [options.stage] + * @example + * var animation = new Animation(); + * var obj = { + * x: 100, + * y: 100 + * }; + * animation.animate(node.position) + * .when(1000, { + * x: 500, + * y: 500 + * }) + * .when(2000, { + * x: 100, + * y: 100 + * }) + * .start('spline'); + */ + var Animation = function (options) { + + options = options || {}; + + this.stage = options.stage || {}; + + this.onframe = options.onframe || function() {}; + + // private properties + this._clips = []; + + this._running = false; + + this._time; + + this._pausedTime; + + this._pauseStart; + + this._paused = false; + + Dispatcher.call(this); + }; + + Animation.prototype = { + + constructor: Animation, + /** + * 添加 clip + * @param {module:zrender/animation/Clip} clip + */ + addClip: function (clip) { + this._clips.push(clip); + }, + /** + * 添加 animator + * @param {module:zrender/animation/Animator} animator + */ + addAnimator: function (animator) { + animator.animation = this; + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.addClip(clips[i]); + } + }, + /** + * 删除动画片段 + * @param {module:zrender/animation/Clip} clip + */ + removeClip: function(clip) { + var idx = util.indexOf(this._clips, clip); + if (idx >= 0) { + this._clips.splice(idx, 1); + } + }, + + /** + * 删除动画片段 + * @param {module:zrender/animation/Animator} animator + */ + removeAnimator: function (animator) { + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.removeClip(clips[i]); + } + animator.animation = null; + }, + + _update: function() { + + var time = new Date().getTime() - this._pausedTime; + var delta = time - this._time; + var clips = this._clips; + var len = clips.length; + + var deferredEvents = []; + var deferredClips = []; + for (var i = 0; i < len; i++) { + var clip = clips[i]; + var e = clip.step(time); + // Throw out the events need to be called after + // stage.update, like destroy + if (e) { + deferredEvents.push(e); + deferredClips.push(clip); + } + } + + // Remove the finished clip + for (var i = 0; i < len;) { + if (clips[i]._needsRemove) { + clips[i] = clips[len - 1]; + clips.pop(); + len--; + } + else { + i++; + } + } + + len = deferredEvents.length; + for (var i = 0; i < len; i++) { + deferredClips[i].fire(deferredEvents[i]); + } + + this._time = time; + + this.onframe(delta); + + this.trigger('frame', delta); + + if (this.stage.update) { + this.stage.update(); + } + }, + + _startLoop: function () { + var self = this; + + this._running = true; + + function step() { + if (self._running) { + + requestAnimationFrame(step); + + !self._paused && self._update(); + } + } + + requestAnimationFrame(step); + }, + + /** + * 开始运行动画 + */ + start: function () { + + this._time = new Date().getTime(); + this._pausedTime = 0; + + this._startLoop(); + }, + /** + * 停止运行动画 + */ + stop: function () { + this._running = false; + }, + + /** + * Pause + */ + pause: function () { + if (!this._paused) { + this._pauseStart = new Date().getTime(); + this._paused = true; + } + }, + + /** + * Resume + */ + resume: function () { + if (this._paused) { + this._pausedTime += (new Date().getTime()) - this._pauseStart; + this._paused = false; + } + }, + + /** + * 清除所有动画片段 + */ + clear: function () { + this._clips = []; + }, + /** + * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画 + * @param {Object} target + * @param {Object} options + * @param {boolean} [options.loop=false] 是否循环播放动画 + * @param {Function} [options.getter=null] + * 如果指定getter函数,会通过getter函数取属性值 + * @param {Function} [options.setter=null] + * 如果指定setter函数,会通过setter函数设置属性值 + * @return {module:zrender/animation/Animation~Animator} + */ + // TODO Gap + animate: function (target, options) { + options = options || {}; + var animator = new Animator( + target, + options.loop, + options.getter, + options.setter + ); + + return animator; + } + }; + + util.mixin(Animation, Dispatcher); + + module.exports = Animation; + + + +/***/ }, +/* 87 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 事件辅助类 + * @module zrender/core/event + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + + + var Eventful = __webpack_require__(33); + + var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; + + function getBoundingClientRect(el) { + // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect + return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0}; + } + + function clientToLocal(el, e, out) { + // clientX/clientY is according to view port. + var box = getBoundingClientRect(el); + out = out || {}; + out.zrX = e.clientX - box.left; + out.zrY = e.clientY - box.top; + return out; + } + + /** + * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标 + */ + function normalizeEvent(el, e) { + + e = e || window.event; + + if (e.zrX != null) { + return e; + } + + var eventType = e.type; + var isTouch = eventType && eventType.indexOf('touch') >= 0; + + if (!isTouch) { + clientToLocal(el, e, e); + e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3; + } + else { + var touch = eventType != 'touchend' + ? e.targetTouches[0] + : e.changedTouches[0]; + touch && clientToLocal(el, touch, e); + } + + return e; + } + + function addEventListener(el, name, handler) { + if (isDomLevel2) { + el.addEventListener(name, handler); + } + else { + el.attachEvent('on' + name, handler); + } + } + + function removeEventListener(el, name, handler) { + if (isDomLevel2) { + el.removeEventListener(name, handler); + } + else { + el.detachEvent('on' + name, handler); + } + } + + /** + * 停止冒泡和阻止默认行为 + * @memberOf module:zrender/core/event + * @method + * @param {Event} e : event对象 + */ + var stop = isDomLevel2 + ? function (e) { + e.preventDefault(); + e.stopPropagation(); + e.cancelBubble = true; + } + : function (e) { + e.returnValue = false; + e.cancelBubble = true; + }; + + module.exports = { + clientToLocal: clientToLocal, + normalizeEvent: normalizeEvent, + addEventListener: addEventListener, + removeEventListener: removeEventListener, + + stop: stop, + // 做向上兼容 + Dispatcher: Eventful + }; + + + +/***/ }, +/* 88 */ +/***/ function(module, exports) { + + + + module.exports = (typeof window !== 'undefined' && + (window.requestAnimationFrame + || window.msRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame)) + || function (func) { + setTimeout(func, 16); + }; + + + +/***/ }, +/* 89 */ +/***/ function(module, exports, __webpack_require__) { + + + + var eventTool = __webpack_require__(87); + var zrUtil = __webpack_require__(4); + var Eventful = __webpack_require__(33); + var env = __webpack_require__(2); + var GestureMgr = __webpack_require__(90); + + var addEventListener = eventTool.addEventListener; + var removeEventListener = eventTool.removeEventListener; + var normalizeEvent = eventTool.normalizeEvent; + + var TOUCH_CLICK_DELAY = 300; + + var mouseHandlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove' + ]; + + var touchHandlerNames = [ + 'touchstart', 'touchend', 'touchmove' + ]; + + function eventNameFix(name) { + return (name === 'mousewheel' && env.browser.firefox) ? 'DOMMouseScroll' : name; + } + + function processGesture(proxy, event, stage) { + var gestureMgr = proxy._gestureMgr; + + stage === 'start' && gestureMgr.clear(); + + var gestureInfo = gestureMgr.recognize( + event, + proxy.handler.findHover(event.zrX, event.zrY, null), + proxy.dom + ); + + stage === 'end' && gestureMgr.clear(); + + if (gestureInfo) { + // eventTool.stop(event); + var type = gestureInfo.type; + event.gestureEvent = type; + + proxy.handler.dispatchToElement(gestureInfo.target, type, gestureInfo.event); + } + } + + /** + * Prevent mouse event from being dispatched after Touch Events action + * @see + * 1. Mobile browsers dispatch mouse events 300ms after touchend. + * 2. Chrome for Android dispatch mousedown for long-touch about 650ms + * Result: Blocking Mouse Events for 700ms. + */ + function setTouchTimer(instance) { + instance._touching = true; + clearTimeout(instance._touchTimer); + instance._touchTimer = setTimeout(function () { + instance._touching = false; + }, 700); + } + + function useTouchEvent() { + return env.touchEventsSupported; + } + + var domHandlers = { + /** + * Mouse move handler + * @inner + * @param {Event} event + */ + mousemove: function (event) { + event = normalizeEvent(this.dom, event); + + this.trigger('mousemove', event); + }, + + /** + * Mouse out handler + * @inner + * @param {Event} event + */ + mouseout: function (event) { + event = normalizeEvent(this.dom, event); + + var element = event.toElement || event.relatedTarget; + if (element != this.dom) { + while (element && element.nodeType != 9) { + // 忽略包含在root中的dom引起的mouseOut + if (element === this.dom) { + return; + } + + element = element.parentNode; + } + } + + this.trigger('mouseout', event); + }, + + /** + * Touch开始响应函数 + * @inner + * @param {Event} event + */ + touchstart: function (event) { + // Default mouse behaviour should not be disabled here. + // For example, page may needs to be slided. + + event = normalizeEvent(this.dom, event); + + this._lastTouchMoment = new Date(); + + processGesture(this, event, 'start'); + + // 平板补充一次findHover + // this._mobileFindFixed(event); + // Trigger mousemove and mousedown + domHandlers.mousemove.call(this, event); + + domHandlers.mousedown.call(this, event); + + setTouchTimer(this); + }, + + /** + * Touch移动响应函数 + * @inner + * @param {Event} event + */ + touchmove: function (event) { + + event = normalizeEvent(this.dom, event); + + processGesture(this, event, 'change'); + + // Mouse move should always be triggered no matter whether + // there is gestrue event, because mouse move and pinch may + // be used at the same time. + domHandlers.mousemove.call(this, event); + + setTouchTimer(this); + }, + + /** + * Touch结束响应函数 + * @inner + * @param {Event} event + */ + touchend: function (event) { + + event = normalizeEvent(this.dom, event); + + processGesture(this, event, 'end'); + + domHandlers.mouseup.call(this, event); + + // click event should always be triggered no matter whether + // there is gestrue event. System click can not be prevented. + if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) { + domHandlers.click.call(this, event); + } + + setTouchTimer(this); + } + }; + + // Common handlers + zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick'], function (name) { + domHandlers[name] = function (event) { + event = normalizeEvent(this.dom, event); + this.trigger(name, event); + }; + }); + + /** + * 为控制类实例初始化dom 事件处理函数 + * + * @inner + * @param {module:zrender/Handler} instance 控制类实例 + */ + function initDomHandler(instance) { + for (var i = 0; i < touchHandlerNames.length; i++) { + var name = touchHandlerNames[i]; + instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); + } + + for (var i = 0; i < mouseHandlerNames.length; i++) { + var name = mouseHandlerNames[i]; + instance._handlers[name] = makeMouseHandler(domHandlers[name], instance); + } + + function makeMouseHandler(fn, instance) { + return function () { + if (instance._touching) { + return; + } + return fn.apply(instance, arguments); + }; + } + } + + + function HandlerDomProxy(dom) { + Eventful.call(this); + + this.dom = dom; + + /** + * @private + * @type {boolean} + */ + this._touching = false; + + /** + * @private + * @type {number} + */ + this._touchTimer; + + /** + * @private + * @type {module:zrender/core/GestureMgr} + */ + this._gestureMgr = new GestureMgr(); + + this._handlers = {}; + + initDomHandler(this); + + if (useTouchEvent()) { + mountHandlers(touchHandlerNames, this); + + // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. + // addEventListener(root, 'mouseout', this._mouseoutHandler); + } + + // Considering some devices that both enable touch and mouse event (like MS Surface + // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise + // mouse event can not be handle in those devices. + mountHandlers(mouseHandlerNames, this); + + function mountHandlers(handlerNames, instance) { + zrUtil.each(handlerNames, function (name) { + addEventListener(dom, eventNameFix(name), instance._handlers[name]); + }, instance); + } + } + + var handlerDomProxyProto = HandlerDomProxy.prototype; + handlerDomProxyProto.dispose = function () { + var handlerNames = mouseHandlerNames.concat(touchHandlerNames); + + for (var i = 0; i < handlerNames.length; i++) { + var name = handlerNames[i]; + removeEventListener(this.dom, eventNameFix(name), this._handlers[name]); + } + }; + + handlerDomProxyProto.setCursor = function (cursorStyle) { + this.dom.style.cursor = cursorStyle || 'default'; + }; + + zrUtil.mixin(HandlerDomProxy, Eventful); + + module.exports = HandlerDomProxy; + + +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Only implements needed gestures for mobile. + */ + + + var eventUtil = __webpack_require__(87); + + var GestureMgr = function () { + + /** + * @private + * @type {Array.} + */ + this._track = []; + }; + + GestureMgr.prototype = { + + constructor: GestureMgr, + + recognize: function (event, target, root) { + this._doTrack(event, target, root); + return this._recognize(event); + }, + + clear: function () { + this._track.length = 0; + return this; + }, + + _doTrack: function (event, target, root) { + var touches = event.touches; + + if (!touches) { + return; + } + + var trackItem = { + points: [], + touches: [], + target: target, + event: event + }; + + for (var i = 0, len = touches.length; i < len; i++) { + var touch = touches[i]; + var pos = eventUtil.clientToLocal(root, touch); + trackItem.points.push([pos.zrX, pos.zrY]); + trackItem.touches.push(touch); + } + + this._track.push(trackItem); + }, + + _recognize: function (event) { + for (var eventName in recognizers) { + if (recognizers.hasOwnProperty(eventName)) { + var gestureInfo = recognizers[eventName](this._track, event); + if (gestureInfo) { + return gestureInfo; + } + } + } + } + }; + + function dist(pointPair) { + var dx = pointPair[1][0] - pointPair[0][0]; + var dy = pointPair[1][1] - pointPair[0][1]; + + return Math.sqrt(dx * dx + dy * dy); + } + + function center(pointPair) { + return [ + (pointPair[0][0] + pointPair[1][0]) / 2, + (pointPair[0][1] + pointPair[1][1]) / 2 + ]; + } + + var recognizers = { + + pinch: function (track, event) { + var trackLen = track.length; + + if (!trackLen) { + return; + } + + var pinchEnd = (track[trackLen - 1] || {}).points; + var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd; + + if (pinchPre + && pinchPre.length > 1 + && pinchEnd + && pinchEnd.length > 1 + ) { + var pinchScale = dist(pinchEnd) / dist(pinchPre); + !isFinite(pinchScale) && (pinchScale = 1); + + event.pinchScale = pinchScale; + + var pinchCenter = center(pinchEnd); + event.pinchX = pinchCenter[0]; + event.pinchY = pinchCenter[1]; + + return { + type: 'pinch', + target: track[0].target, + event: event + }; + } + } + + // Only pinch currently. + }; + + module.exports = GestureMgr; + + + +/***/ }, +/* 91 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Default canvas painter + * @module zrender/Painter + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + * pissang (https://www.github.com/pissang) + */ + + + var config = __webpack_require__(41); + var util = __webpack_require__(4); + var log = __webpack_require__(40); + var BoundingRect = __webpack_require__(9); + var timsort = __webpack_require__(85); + + var Layer = __webpack_require__(92); + + var requestAnimationFrame = __webpack_require__(88); + + // PENDIGN + // Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time. + // + // Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer. + var MAX_PROGRESSIVE_LAYER_NUMBER = 5; + + function parseInt10(val) { + return parseInt(val, 10); + } + + function isLayerValid(layer) { + if (!layer) { + return false; + } + + if (layer.isBuildin) { + return true; + } + + if (typeof(layer.resize) !== 'function' + || typeof(layer.refresh) !== 'function' + ) { + return false; + } + + return true; + } + + function preProcessLayer(layer) { + layer.__unusedCount++; + } + + function postProcessLayer(layer) { + if (layer.__unusedCount == 1) { + layer.clear(); + } + } + + var tmpRect = new BoundingRect(0, 0, 0, 0); + var viewRect = new BoundingRect(0, 0, 0, 0); + function isDisplayableCulled(el, width, height) { + tmpRect.copy(el.getBoundingRect()); + if (el.transform) { + tmpRect.applyTransform(el.transform); + } + viewRect.width = width; + viewRect.height = height; + return !tmpRect.intersect(viewRect); + } + + function isClipPathChanged(clipPaths, prevClipPaths) { + if (clipPaths == prevClipPaths) { // Can both be null or undefined + return false; + } + + if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) { + return true; + } + for (var i = 0; i < clipPaths.length; i++) { + if (clipPaths[i] !== prevClipPaths[i]) { + return true; + } + } + } + + function doClip(clipPaths, ctx) { + for (var i = 0; i < clipPaths.length; i++) { + var clipPath = clipPaths[i]; + var path = clipPath.path; + + clipPath.setTransform(ctx); + path.beginPath(ctx); + clipPath.buildPath(path, clipPath.shape); + ctx.clip(); + // Transform back + clipPath.restoreTransform(ctx); + } + } + + function createRoot(width, height) { + var domRoot = document.createElement('div'); + var domRootStyle = domRoot.style; + + // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬 + domRootStyle.position = 'relative'; + domRootStyle.overflow = 'hidden'; + domRootStyle.width = width + 'px'; + domRootStyle.height = height + 'px'; + return domRoot; + } + + /** + * @alias module:zrender/Painter + * @constructor + * @param {HTMLElement} root 绘图容器 + * @param {module:zrender/Storage} storage + * @param {Ojbect} opts + */ + var Painter = function (root, storage, opts) { + // In node environment using node-canvas + var singleCanvas = !root.nodeName // In node ? + || root.nodeName.toUpperCase() === 'CANVAS'; + + opts = opts || {}; + + /** + * @type {number} + */ + this.dpr = opts.devicePixelRatio || config.devicePixelRatio; + /** + * @type {boolean} + * @private + */ + this._singleCanvas = singleCanvas; + /** + * 绘图容器 + * @type {HTMLElement} + */ + this.root = root; + + var rootStyle = root.style; + + if (rootStyle) { + rootStyle['-webkit-tap-highlight-color'] = 'transparent'; + rootStyle['-webkit-user-select'] = + rootStyle['user-select'] = + rootStyle['-webkit-touch-callout'] = 'none'; + + root.innerHTML = ''; + } + + /** + * @type {module:zrender/Storage} + */ + this.storage = storage; + + /** + * @type {Array.} + * @private + */ + var zlevelList = this._zlevelList = []; + + /** + * @type {Object.} + * @private + */ + var layers = this._layers = {}; + + /** + * @type {Object.} + * @type {private} + */ + this._layerConfig = {}; + + if (!singleCanvas) { + this._width = this._getWidth(); + this._height = this._getHeight(); + + var domRoot = this._domRoot = createRoot( + this._width, this._height + ); + root.appendChild(domRoot); + } + else { + // Use canvas width and height directly + var width = root.width; + var height = root.height; + this._width = width; + this._height = height; + + // Create layer if only one given canvas + // Device pixel ratio is fixed to 1 because given canvas has its specified width and height + var mainLayer = new Layer(root, this, 1); + mainLayer.initContext(); + // FIXME Use canvas width and height + // mainLayer.resize(width, height); + layers[0] = mainLayer; + zlevelList.push(0); + } + + this.pathToImage = this._createPathToImage(); + + // Layers for progressive rendering + this._progressiveLayers = []; + + /** + * @type {module:zrender/Layer} + * @private + */ + this._hoverlayer; + + this._hoverElements = []; + }; + + Painter.prototype = { + + constructor: Painter, + + /** + * If painter use a single canvas + * @return {boolean} + */ + isSingleCanvas: function () { + return this._singleCanvas; + }, + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._singleCanvas ? this._layers[0].dom : this._domRoot; + }, + + /** + * 刷新 + * @param {boolean} [paintAll=false] 强制绘制所有displayable + */ + refresh: function (paintAll) { + + var list = this.storage.getDisplayList(true); + + var zlevelList = this._zlevelList; + + this._paintList(list, paintAll); + + // Paint custum layers + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (!layer.isBuildin && layer.refresh) { + layer.refresh(); + } + } + + this.refreshHover(); + + if (this._progressiveLayers.length) { + this._startProgessive(); + } + + return this; + }, + + addHover: function (el, hoverStyle) { + if (el.__hoverMir) { + return; + } + var elMirror = new el.constructor({ + style: el.style, + shape: el.shape + }); + elMirror.__from = el; + el.__hoverMir = elMirror; + elMirror.setStyle(hoverStyle); + this._hoverElements.push(elMirror); + }, + + removeHover: function (el) { + var elMirror = el.__hoverMir; + var hoverElements = this._hoverElements; + var idx = util.indexOf(hoverElements, elMirror); + if (idx >= 0) { + hoverElements.splice(idx, 1); + } + el.__hoverMir = null; + }, + + clearHover: function (el) { + var hoverElements = this._hoverElements; + for (var i = 0; i < hoverElements.length; i++) { + var from = hoverElements[i].__from; + if (from) { + from.__hoverMir = null; + } + } + hoverElements.length = 0; + }, + + refreshHover: function () { + var hoverElements = this._hoverElements; + var len = hoverElements.length; + var hoverLayer = this._hoverlayer; + hoverLayer && hoverLayer.clear(); + + if (!len) { + return; + } + timsort(hoverElements, this.storage.displayableSortFunc); + + // Use a extream large zlevel + // FIXME? + if (!hoverLayer) { + hoverLayer = this._hoverlayer = this.getLayer(1e5); + } + + var scope = {}; + hoverLayer.ctx.save(); + for (var i = 0; i < len;) { + var el = hoverElements[i]; + var originalEl = el.__from; + // Original el is removed + // PENDING + if (!(originalEl && originalEl.__zr)) { + hoverElements.splice(i, 1); + originalEl.__hoverMir = null; + len--; + continue; + } + i++; + + // Use transform + // FIXME style and shape ? + if (!originalEl.invisible) { + el.transform = originalEl.transform; + el.invTransform = originalEl.invTransform; + el.__clipPaths = originalEl.__clipPaths; + // el. + this._doPaintEl(el, hoverLayer, true, scope); + } + } + hoverLayer.ctx.restore(); + }, + + _startProgessive: function () { + var self = this; + + if (!self._furtherProgressive) { + return; + } + + // Use a token to stop progress steps triggered by + // previous zr.refresh calling. + var token = self._progressiveToken = +new Date(); + + self._progress++; + requestAnimationFrame(step); + + function step() { + // In case refreshed or disposed + if (token === self._progressiveToken && self.storage) { + + self._doPaintList(self.storage.getDisplayList()); + + if (self._furtherProgressive) { + self._progress++; + requestAnimationFrame(step); + } + else { + self._progressiveToken = -1; + } + } + } + }, + + _clearProgressive: function () { + this._progressiveToken = -1; + this._progress = 0; + util.each(this._progressiveLayers, function (layer) { + layer.__dirty && layer.clear(); + }); + }, + + _paintList: function (list, paintAll) { + + if (paintAll == null) { + paintAll = false; + } + + this._updateLayerStatus(list); + + this._clearProgressive(); + + this.eachBuildinLayer(preProcessLayer); + + this._doPaintList(list, paintAll); + + this.eachBuildinLayer(postProcessLayer); + }, + + _doPaintList: function (list, paintAll) { + var currentLayer; + var currentZLevel; + var ctx; + + // var invTransform = []; + var scope; + + var progressiveLayerIdx = 0; + var currentProgressiveLayer; + + var width = this._width; + var height = this._height; + var layerProgress; + var frame = this._progress; + function flushProgressiveLayer(layer) { + var dpr = ctx.dpr || 1; + ctx.save(); + ctx.globalAlpha = 1; + ctx.shadowBlur = 0; + // Avoid layer don't clear in next progressive frame + currentLayer.__dirty = true; + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr); + ctx.restore(); + } + + for (var i = 0, l = list.length; i < l; i++) { + var el = list[i]; + var elZLevel = this._singleCanvas ? 0 : el.zlevel; + + var elFrame = el.__frame; + + // Flush at current context + // PENDING + if (elFrame < 0 && currentProgressiveLayer) { + flushProgressiveLayer(currentProgressiveLayer); + currentProgressiveLayer = null; + } + + // Change draw layer + if (currentZLevel !== elZLevel) { + if (ctx) { + ctx.restore(); + } + + // Reset scope + scope = {}; + + // Only 0 zlevel if only has one canvas + currentZLevel = elZLevel; + currentLayer = this.getLayer(currentZLevel); + + if (!currentLayer.isBuildin) { + log( + 'ZLevel ' + currentZLevel + + ' has been used by unkown layer ' + currentLayer.id + ); + } + + ctx = currentLayer.ctx; + ctx.save(); + + // Reset the count + currentLayer.__unusedCount = 0; + + if (currentLayer.__dirty || paintAll) { + currentLayer.clear(); + } + } + + if (!(currentLayer.__dirty || paintAll)) { + continue; + } + + if (elFrame >= 0) { + // Progressive layer changed + if (!currentProgressiveLayer) { + currentProgressiveLayer = this._progressiveLayers[ + Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1) + ]; + + currentProgressiveLayer.ctx.save(); + currentProgressiveLayer.renderScope = {}; + + if (currentProgressiveLayer + && (currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress) + ) { + // flushProgressiveLayer(currentProgressiveLayer); + // Quick jump all progressive elements + // All progressive element are not dirty, jump over and flush directly + i = currentProgressiveLayer.__nextIdxNotProg - 1; + // currentProgressiveLayer = null; + continue; + } + + layerProgress = currentProgressiveLayer.__progress; + + if (!currentProgressiveLayer.__dirty) { + // Keep rendering + frame = layerProgress; + } + + currentProgressiveLayer.__progress = frame + 1; + } + + if (elFrame === frame) { + this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope); + } + } + else { + this._doPaintEl(el, currentLayer, paintAll, scope); + } + + el.__dirty = false; + } + + if (currentProgressiveLayer) { + flushProgressiveLayer(currentProgressiveLayer); + } + + // Restore the lastLayer ctx + ctx && ctx.restore(); + // If still has clipping state + // if (scope.prevElClipPaths) { + // ctx.restore(); + // } + + this._furtherProgressive = false; + util.each(this._progressiveLayers, function (layer) { + if (layer.__maxProgress >= layer.__progress) { + this._furtherProgressive = true; + } + }, this); + }, + + _doPaintEl: function (el, currentLayer, forcePaint, scope) { + var ctx = currentLayer.ctx; + var m = el.transform; + if ( + (currentLayer.__dirty || forcePaint) + // Ignore invisible element + && !el.invisible + // Ignore transparent element + && el.style.opacity !== 0 + // Ignore scale 0 element, in some environment like node-canvas + // Draw a scale 0 element can cause all following draw wrong + // And setTransform with scale 0 will cause set back transform failed. + && !(m && !m[0] && !m[3]) + // Ignore culled element + && !(el.culling && isDisplayableCulled(el, this._width, this._height)) + ) { + + var clipPaths = el.__clipPaths; + + // Optimize when clipping on group with several elements + if (scope.prevClipLayer !== currentLayer + || isClipPathChanged(clipPaths, scope.prevElClipPaths) + ) { + // If has previous clipping state, restore from it + if (scope.prevElClipPaths) { + scope.prevClipLayer.ctx.restore(); + scope.prevClipLayer = scope.prevElClipPaths = null; + + // Reset prevEl since context has been restored + scope.prevEl = null; + } + // New clipping state + if (clipPaths) { + ctx.save(); + doClip(clipPaths, ctx); + scope.prevClipLayer = currentLayer; + scope.prevElClipPaths = clipPaths; + } + } + el.beforeBrush && el.beforeBrush(ctx); + + el.brush(ctx, scope.prevEl || null); + scope.prevEl = el; + + el.afterBrush && el.afterBrush(ctx); + } + }, + + /** + * 获取 zlevel 所在层,如果不存在则会创建一个新的层 + * @param {number} zlevel + * @return {module:zrender/Layer} + */ + getLayer: function (zlevel) { + if (this._singleCanvas) { + return this._layers[0]; + } + + var layer = this._layers[zlevel]; + if (!layer) { + // Create a new layer + layer = new Layer('zr_' + zlevel, this, this.dpr); + layer.isBuildin = true; + + if (this._layerConfig[zlevel]) { + util.merge(layer, this._layerConfig[zlevel], true); + } + + this.insertLayer(zlevel, layer); + + // Context is created after dom inserted to document + // Or excanvas will get 0px clientWidth and clientHeight + layer.initContext(); + } + + return layer; + }, + + insertLayer: function (zlevel, layer) { + + var layersMap = this._layers; + var zlevelList = this._zlevelList; + var len = zlevelList.length; + var prevLayer = null; + var i = -1; + var domRoot = this._domRoot; + + if (layersMap[zlevel]) { + log('ZLevel ' + zlevel + ' has been used already'); + return; + } + // Check if is a valid layer + if (!isLayerValid(layer)) { + log('Layer of zlevel ' + zlevel + ' is not valid'); + return; + } + + if (len > 0 && zlevel > zlevelList[0]) { + for (i = 0; i < len - 1; i++) { + if ( + zlevelList[i] < zlevel + && zlevelList[i + 1] > zlevel + ) { + break; + } + } + prevLayer = layersMap[zlevelList[i]]; + } + zlevelList.splice(i + 1, 0, zlevel); + + if (prevLayer) { + var prevDom = prevLayer.dom; + if (prevDom.nextSibling) { + domRoot.insertBefore( + layer.dom, + prevDom.nextSibling + ); + } + else { + domRoot.appendChild(layer.dom); + } + } + else { + if (domRoot.firstChild) { + domRoot.insertBefore(layer.dom, domRoot.firstChild); + } + else { + domRoot.appendChild(layer.dom); + } + } + + layersMap[zlevel] = layer; + }, + + // Iterate each layer + eachLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + cb.call(context, this._layers[z], z); + } + }, + + // Iterate each buildin layer + eachBuildinLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var layer; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + layer = this._layers[z]; + if (layer.isBuildin) { + cb.call(context, layer, z); + } + } + }, + + // Iterate each other layer except buildin layer + eachOtherLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var layer; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + layer = this._layers[z]; + if (! layer.isBuildin) { + cb.call(context, layer, z); + } + } + }, + + /** + * 获取所有已创建的层 + * @param {Array.} [prevLayer] + */ + getLayers: function () { + return this._layers; + }, + + _updateLayerStatus: function (list) { + + var layers = this._layers; + var progressiveLayers = this._progressiveLayers; + + var elCountsLastFrame = {}; + var progressiveElCountsLastFrame = {}; + + this.eachBuildinLayer(function (layer, z) { + elCountsLastFrame[z] = layer.elCount; + layer.elCount = 0; + layer.__dirty = false; + }); + + util.each(progressiveLayers, function (layer, idx) { + progressiveElCountsLastFrame[idx] = layer.elCount; + layer.elCount = 0; + layer.__dirty = false; + }); + + var progressiveLayerCount = 0; + var currentProgressiveLayer; + var lastProgressiveKey; + var frameCount = 0; + for (var i = 0, l = list.length; i < l; i++) { + var el = list[i]; + var zlevel = this._singleCanvas ? 0 : el.zlevel; + var layer = layers[zlevel]; + var elProgress = el.progressive; + if (layer) { + layer.elCount++; + layer.__dirty = layer.__dirty || el.__dirty; + } + + /////// Update progressive + if (elProgress >= 0) { + // Fix wrong progressive sequence problem. + if (lastProgressiveKey !== elProgress) { + lastProgressiveKey = elProgress; + frameCount++; + } + var elFrame = el.__frame = frameCount - 1; + if (!currentProgressiveLayer) { + var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1); + currentProgressiveLayer = progressiveLayers[idx]; + if (!currentProgressiveLayer) { + currentProgressiveLayer = progressiveLayers[idx] = new Layer( + 'progressive', this, this.dpr + ); + currentProgressiveLayer.initContext(); + } + currentProgressiveLayer.__maxProgress = 0; + } + currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty; + currentProgressiveLayer.elCount++; + + currentProgressiveLayer.__maxProgress = Math.max( + currentProgressiveLayer.__maxProgress, elFrame + ); + + if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) { + // Should keep rendering this layer because progressive rendering is not finished yet + layer.__dirty = true; + } + } + else { + el.__frame = -1; + + if (currentProgressiveLayer) { + currentProgressiveLayer.__nextIdxNotProg = i; + progressiveLayerCount++; + currentProgressiveLayer = null; + } + } + } + + if (currentProgressiveLayer) { + progressiveLayerCount++; + currentProgressiveLayer.__nextIdxNotProg = i; + } + + // 层中的元素数量有发生变化 + this.eachBuildinLayer(function (layer, z) { + if (elCountsLastFrame[z] !== layer.elCount) { + layer.__dirty = true; + } + }); + + progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER); + util.each(progressiveLayers, function (layer, idx) { + if (progressiveElCountsLastFrame[idx] !== layer.elCount) { + el.__dirty = true; + } + if (layer.__dirty) { + layer.__progress = 0; + } + }); + }, + + /** + * 清除hover层外所有内容 + */ + clear: function () { + this.eachBuildinLayer(this._clearLayer); + return this; + }, + + _clearLayer: function (layer) { + layer.clear(); + }, + + /** + * 修改指定zlevel的绘制参数 + * + * @param {string} zlevel + * @param {Object} config 配置对象 + * @param {string} [config.clearColor=0] 每次清空画布的颜色 + * @param {string} [config.motionBlur=false] 是否开启动态模糊 + * @param {number} [config.lastFrameAlpha=0.7] + * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显 + */ + configLayer: function (zlevel, config) { + if (config) { + var layerConfig = this._layerConfig; + if (!layerConfig[zlevel]) { + layerConfig[zlevel] = config; + } + else { + util.merge(layerConfig[zlevel], config, true); + } + + var layer = this._layers[zlevel]; + + if (layer) { + util.merge(layer, layerConfig[zlevel], true); + } + } + }, + + /** + * 删除指定层 + * @param {number} zlevel 层所在的zlevel + */ + delLayer: function (zlevel) { + var layers = this._layers; + var zlevelList = this._zlevelList; + var layer = layers[zlevel]; + if (!layer) { + return; + } + layer.dom.parentNode.removeChild(layer.dom); + delete layers[zlevel]; + + zlevelList.splice(util.indexOf(zlevelList, zlevel), 1); + }, + + /** + * 区域大小变化后重绘 + */ + resize: function (width, height) { + var domRoot = this._domRoot; + // FIXME Why ? + domRoot.style.display = 'none'; + + width = width || this._getWidth(); + height = height || this._getHeight(); + + domRoot.style.display = ''; + + // 优化没有实际改变的resize + if (this._width != width || height != this._height) { + domRoot.style.width = width + 'px'; + domRoot.style.height = height + 'px'; + + for (var id in this._layers) { + this._layers[id].resize(width, height); + } + + this.refresh(true); + } + + this._width = width; + this._height = height; + + return this; + }, + + /** + * 清除单独的一个层 + * @param {number} zlevel + */ + clearLayer: function (zlevel) { + var layer = this._layers[zlevel]; + if (layer) { + layer.clear(); + } + }, + + /** + * 释放 + */ + dispose: function () { + this.root.innerHTML = ''; + + this.root = + this.storage = + + this._domRoot = + this._layers = null; + }, + + /** + * Get canvas which has all thing rendered + * @param {Object} opts + * @param {string} [opts.backgroundColor] + */ + getRenderedCanvas: function (opts) { + opts = opts || {}; + if (this._singleCanvas) { + return this._layers[0].dom; + } + + var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); + imageLayer.initContext(); + + imageLayer.clearColor = opts.backgroundColor; + imageLayer.clear(); + + var displayList = this.storage.getDisplayList(true); + + var scope = {}; + for (var i = 0; i < displayList.length; i++) { + var el = displayList[i]; + this._doPaintEl(el, imageLayer, true, scope); + } + + return imageLayer.dom; + }, + /** + * 获取绘图区域宽度 + */ + getWidth: function () { + return this._width; + }, + + /** + * 获取绘图区域高度 + */ + getHeight: function () { + return this._height; + }, + + _getWidth: function () { + var root = this.root; + var stl = document.defaultView.getComputedStyle(root); + + // FIXME Better way to get the width and height when element has not been append to the document + return ((root.clientWidth || parseInt10(stl.width) || parseInt10(root.style.width)) + - (parseInt10(stl.paddingLeft) || 0) + - (parseInt10(stl.paddingRight) || 0)) | 0; + }, + + _getHeight: function () { + var root = this.root; + var stl = document.defaultView.getComputedStyle(root); + + return ((root.clientHeight || parseInt10(stl.height) || parseInt10(root.style.height)) + - (parseInt10(stl.paddingTop) || 0) + - (parseInt10(stl.paddingBottom) || 0)) | 0; + }, + + _pathToImage: function (id, path, width, height, dpr) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + canvas.width = width * dpr; + canvas.height = height * dpr; + + ctx.clearRect(0, 0, width * dpr, height * dpr); + + var pathTransform = { + position: path.position, + rotation: path.rotation, + scale: path.scale + }; + path.position = [0, 0, 0]; + path.rotation = 0; + path.scale = [1, 1]; + if (path) { + path.brush(ctx); + } + + var ImageShape = __webpack_require__(62); + var imgShape = new ImageShape({ + id: id, + style: { + x: 0, + y: 0, + image: canvas + } + }); + + if (pathTransform.position != null) { + imgShape.position = path.position = pathTransform.position; + } + + if (pathTransform.rotation != null) { + imgShape.rotation = path.rotation = pathTransform.rotation; + } + + if (pathTransform.scale != null) { + imgShape.scale = path.scale = pathTransform.scale; + } + + return imgShape; + }, + + _createPathToImage: function () { + var me = this; + + return function (id, e, width, height) { + return me._pathToImage( + id, e, width, height, me.dpr + ); + }; + } + }; + + module.exports = Painter; + + + +/***/ }, +/* 92 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module zrender/Layer + * @author pissang(https://www.github.com/pissang) + */ + + + var util = __webpack_require__(4); + var config = __webpack_require__(41); + var Style = __webpack_require__(47); + var Pattern = __webpack_require__(59); + + function returnFalse() { + return false; + } + + /** + * 创建dom + * + * @inner + * @param {string} id dom id 待用 + * @param {string} type dom type,such as canvas, div etc. + * @param {Painter} painter painter instance + * @param {number} number + */ + function createDom(id, type, painter, dpr) { + var newDom = document.createElement(type); + var width = painter.getWidth(); + var height = painter.getHeight(); + + var newDomStyle = newDom.style; + // 没append呢,请原谅我这样写,清晰~ + newDomStyle.position = 'absolute'; + newDomStyle.left = 0; + newDomStyle.top = 0; + newDomStyle.width = width + 'px'; + newDomStyle.height = height + 'px'; + newDom.width = width * dpr; + newDom.height = height * dpr; + + // id不作为索引用,避免可能造成的重名,定义为私有属性 + newDom.setAttribute('data-zr-dom-id', id); + return newDom; + } + + /** + * @alias module:zrender/Layer + * @constructor + * @extends module:zrender/mixin/Transformable + * @param {string} id + * @param {module:zrender/Painter} painter + * @param {number} [dpr] + */ + var Layer = function(id, painter, dpr) { + var dom; + dpr = dpr || config.devicePixelRatio; + if (typeof id === 'string') { + dom = createDom(id, 'canvas', painter, dpr); + } + // Not using isDom because in node it will return false + else if (util.isObject(id)) { + dom = id; + id = dom.id; + } + this.id = id; + this.dom = dom; + + var domStyle = dom.style; + if (domStyle) { // Not in node + dom.onselectstart = returnFalse; // 避免页面选中的尴尬 + domStyle['-webkit-user-select'] = 'none'; + domStyle['user-select'] = 'none'; + domStyle['-webkit-touch-callout'] = 'none'; + domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)'; + } + + this.domBack = null; + this.ctxBack = null; + + this.painter = painter; + + this.config = null; + + // Configs + /** + * 每次清空画布的颜色 + * @type {string} + * @default 0 + */ + this.clearColor = 0; + /** + * 是否开启动态模糊 + * @type {boolean} + * @default false + */ + this.motionBlur = false; + /** + * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显 + * @type {number} + * @default 0.7 + */ + this.lastFrameAlpha = 0.7; + + /** + * Layer dpr + * @type {number} + */ + this.dpr = dpr; + }; + + Layer.prototype = { + + constructor: Layer, + + elCount: 0, + + __dirty: true, + + initContext: function () { + this.ctx = this.dom.getContext('2d'); + + this.ctx.dpr = this.dpr; + }, + + createBackBuffer: function () { + var dpr = this.dpr; + + this.domBack = createDom('back-' + this.id, 'canvas', this.painter, dpr); + this.ctxBack = this.domBack.getContext('2d'); + + if (dpr != 1) { + this.ctxBack.scale(dpr, dpr); + } + }, + + /** + * @param {number} width + * @param {number} height + */ + resize: function (width, height) { + var dpr = this.dpr; + + var dom = this.dom; + var domStyle = dom.style; + var domBack = this.domBack; + + domStyle.width = width + 'px'; + domStyle.height = height + 'px'; + + dom.width = width * dpr; + dom.height = height * dpr; + + if (domBack) { + domBack.width = width * dpr; + domBack.height = height * dpr; + + if (dpr != 1) { + this.ctxBack.scale(dpr, dpr); + } + } + }, + + /** + * 清空该层画布 + * @param {boolean} clearAll Clear all with out motion blur + */ + clear: function (clearAll) { + var dom = this.dom; + var ctx = this.ctx; + var width = dom.width; + var height = dom.height; + + var clearColor = this.clearColor; + var haveMotionBLur = this.motionBlur && !clearAll; + var lastFrameAlpha = this.lastFrameAlpha; + + var dpr = this.dpr; + + if (haveMotionBLur) { + if (!this.domBack) { + this.createBackBuffer(); + } + + this.ctxBack.globalCompositeOperation = 'copy'; + this.ctxBack.drawImage( + dom, 0, 0, + width / dpr, + height / dpr + ); + } + + ctx.clearRect(0, 0, width, height); + if (clearColor) { + var clearColorGradientOrPattern; + // Gradient + if (clearColor.colorStops) { + // Cache canvas gradient + clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, { + x: 0, + y: 0, + width: width, + height: height + }); + + clearColor.__canvasGradient = clearColorGradientOrPattern; + } + // Pattern + else if (clearColor.image) { + clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx); + } + ctx.save(); + ctx.fillStyle = clearColorGradientOrPattern || clearColor; + ctx.fillRect(0, 0, width, height); + ctx.restore(); + } + + if (haveMotionBLur) { + var domBack = this.domBack; + ctx.save(); + ctx.globalAlpha = lastFrameAlpha; + ctx.drawImage(domBack, 0, 0, width, height); + ctx.restore(); + } + } + }; + + module.exports = Layer; + + +/***/ }, +/* 93 */ +/***/ function(module, exports, __webpack_require__) { + + + var Gradient = __webpack_require__(61); + module.exports = function (ecModel) { + function encodeColor(seriesModel) { + var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.'); + var data = seriesModel.getData(); + var color = seriesModel.get(colorAccessPath) // Set in itemStyle + || seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color + + // FIXME Set color function or use the platte color + data.setVisual('color', color); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + if (typeof color === 'function' && !(color instanceof Gradient)) { + data.each(function (idx) { + data.setItemVisual( + idx, 'color', color(seriesModel.getDataParams(idx)) + ); + }); + } + + // itemStyle in each data item + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var color = itemModel.get(colorAccessPath, true); + if (color != null) { + data.setItemVisual(idx, 'color', color); + } + }); + } + } + ecModel.eachRawSeries(encodeColor); + }; + + +/***/ }, +/* 94 */ +/***/ function(module, exports, __webpack_require__) { + + // Compatitable with 2.0 + + + var zrUtil = __webpack_require__(4); + var compatStyle = __webpack_require__(95); + + function get(opt, path) { + path = path.split(','); + var obj = opt; + for (var i = 0; i < path.length; i++) { + obj = obj && obj[path[i]]; + if (obj == null) { + break; + } + } + return obj; + } + + function set(opt, path, val, overwrite) { + path = path.split(','); + var obj = opt; + var key; + for (var i = 0; i < path.length - 1; i++) { + key = path[i]; + if (obj[key] == null) { + obj[key] = {}; + } + obj = obj[key]; + } + if (overwrite || obj[path[i]] == null) { + obj[path[i]] = val; + } + } + + function compatLayoutProperties(option) { + each(LAYOUT_PROPERTIES, function (prop) { + if (prop[0] in option && !(prop[1] in option)) { + option[prop[1]] = option[prop[0]]; + } + }); + } + + var LAYOUT_PROPERTIES = [ + ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom'] + ]; + + var COMPATITABLE_COMPONENTS = [ + 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline' + ]; + + var COMPATITABLE_SERIES = [ + 'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter', + 'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel', + 'pie', 'radar', 'sankey', 'scatter', 'treemap' + ]; + + var each = zrUtil.each; + + module.exports = function (option) { + each(option.series, function (seriesOpt) { + if (!zrUtil.isObject(seriesOpt)) { + return; + } + + var seriesType = seriesOpt.type; + + compatStyle(seriesOpt); + + if (seriesType === 'pie' || seriesType === 'gauge') { + if (seriesOpt.clockWise != null) { + seriesOpt.clockwise = seriesOpt.clockWise; + } + } + if (seriesType === 'gauge') { + var pointerColor = get(seriesOpt, 'pointer.color'); + pointerColor != null + && set(seriesOpt, 'itemStyle.normal.color', pointerColor); + } + + for (var i = 0; i < COMPATITABLE_SERIES.length; i++) { + if (COMPATITABLE_SERIES[i] === seriesOpt.type) { + compatLayoutProperties(seriesOpt); + break; + } + } + }); + + // dataRange has changed to visualMap + if (option.dataRange) { + option.visualMap = option.dataRange; + } + + each(COMPATITABLE_COMPONENTS, function (componentName) { + var options = option[componentName]; + if (options) { + if (!zrUtil.isArray(options)) { + options = [options]; + } + each(options, function (option) { + compatLayoutProperties(option); + }); + } + }); + }; + + +/***/ }, +/* 95 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var POSSIBLE_STYLES = [ + 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', + 'chordStyle', 'label', 'labelLine' + ]; + + function compatItemStyle(opt) { + var itemStyleOpt = opt && opt.itemStyle; + if (itemStyleOpt) { + zrUtil.each(POSSIBLE_STYLES, function (styleName) { + var normalItemStyleOpt = itemStyleOpt.normal; + var emphasisItemStyleOpt = itemStyleOpt.emphasis; + if (normalItemStyleOpt && normalItemStyleOpt[styleName]) { + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].normal) { + opt[styleName].normal = normalItemStyleOpt[styleName]; + } + else { + zrUtil.merge(opt[styleName].normal, normalItemStyleOpt[styleName]); + } + normalItemStyleOpt[styleName] = null; + } + if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) { + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].emphasis) { + opt[styleName].emphasis = emphasisItemStyleOpt[styleName]; + } + else { + zrUtil.merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); + } + emphasisItemStyleOpt[styleName] = null; + } + }); + } + } + + module.exports = function (seriesOpt) { + if (!seriesOpt) { + return; + } + compatItemStyle(seriesOpt); + compatItemStyle(seriesOpt.markPoint); + compatItemStyle(seriesOpt.markLine); + var data = seriesOpt.data; + if (data) { + for (var i = 0; i < data.length; i++) { + compatItemStyle(data[i]); + } + // mark point data + var markPoint = seriesOpt.markPoint; + if (markPoint && markPoint.data) { + var mpData = markPoint.data; + for (var i = 0; i < mpData.length; i++) { + compatItemStyle(mpData[i]); + } + } + // mark line data + var markLine = seriesOpt.markLine; + if (markLine && markLine.data) { + var mlData = markLine.data; + for (var i = 0; i < mlData.length; i++) { + if (zrUtil.isArray(mlData[i])) { + compatItemStyle(mlData[i][0]); + compatItemStyle(mlData[i][1]); + } + else { + compatItemStyle(mlData[i]); + } + } + } + } + }; + + +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var PI = Math.PI; + /** + * @param {module:echarts/ExtensionAPI} api + * @param {Object} [opts] + * @param {string} [opts.text] + * @param {string} [opts.color] + * @param {string} [opts.textColor] + * @return {module:zrender/Element} + */ + module.exports = function (api, opts) { + opts = opts || {}; + zrUtil.defaults(opts, { + text: 'loading', + color: '#c23531', + textColor: '#000', + maskColor: 'rgba(255, 255, 255, 0.8)', + zlevel: 0 + }); + var mask = new graphic.Rect({ + style: { + fill: opts.maskColor + }, + zlevel: opts.zlevel, + z: 10000 + }); + var arc = new graphic.Arc({ + shape: { + startAngle: -PI / 2, + endAngle: -PI / 2 + 0.1, + r: 10 + }, + style: { + stroke: opts.color, + lineCap: 'round', + lineWidth: 5 + }, + zlevel: opts.zlevel, + z: 10001 + }); + var labelRect = new graphic.Rect({ + style: { + fill: 'none', + text: opts.text, + textPosition: 'right', + textDistance: 10, + textFill: opts.textColor + }, + zlevel: opts.zlevel, + z: 10001 + }); + + arc.animateShape(true) + .when(1000, { + endAngle: PI * 3 / 2 + }) + .start('circularInOut'); + arc.animateShape(true) + .when(1000, { + startAngle: PI * 3 / 2 + }) + .delay(300) + .start('circularInOut'); + + var group = new graphic.Group(); + group.add(arc); + group.add(labelRect); + group.add(mask); + // Inject resize + group.resize = function () { + var cx = api.getWidth() / 2; + var cy = api.getHeight() / 2; + arc.setShape({ + cx: cx, + cy: cy + }); + var r = arc.shape.r; + labelRect.setShape({ + x: cx - r, + y: cy - r, + width: r * 2, + height: r * 2 + }); + + mask.setShape({ + x: 0, + y: 0, + width: api.getWidth(), + height: api.getHeight() + }); + }; + group.resize(); + return group; + }; + + +/***/ }, +/* 97 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * List for data storage + * @module echarts/data/List + */ + + + var UNDEFINED = 'undefined'; + var globalObj = typeof window === 'undefined' ? global : window; + var Float64Array = typeof globalObj.Float64Array === UNDEFINED + ? Array : globalObj.Float64Array; + var Int32Array = typeof globalObj.Int32Array === UNDEFINED + ? Array : globalObj.Int32Array; + + var dataCtors = { + 'float': Float64Array, + 'int': Int32Array, + // Ordinal data type can be string or int + 'ordinal': Array, + 'number': Array, + 'time': Array + }; + + var Model = __webpack_require__(12); + var DataDiffer = __webpack_require__(98); + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var isObject = zrUtil.isObject; + + var TRANSFERABLE_PROPERTIES = [ + 'stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData' + ]; + + var transferProperties = function (a, b) { + zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) { + if (b.hasOwnProperty(propName)) { + a[propName] = b[propName]; + } + }); + + a.__wrappedMethods = b.__wrappedMethods; + }; + + /** + * @constructor + * @alias module:echarts/data/List + * + * @param {Array.} dimensions + * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius + * @param {module:echarts/model/Model} hostModel + */ + var List = function (dimensions, hostModel) { + + dimensions = dimensions || ['x', 'y']; + + var dimensionInfos = {}; + var dimensionNames = []; + for (var i = 0; i < dimensions.length; i++) { + var dimensionName; + var dimensionInfo = {}; + if (typeof dimensions[i] === 'string') { + dimensionName = dimensions[i]; + dimensionInfo = { + name: dimensionName, + stackable: false, + // Type can be 'float', 'int', 'number' + // Default is number, Precision of float may not enough + type: 'number' + }; + } + else { + dimensionInfo = dimensions[i]; + dimensionName = dimensionInfo.name; + dimensionInfo.type = dimensionInfo.type || 'number'; + } + dimensionNames.push(dimensionName); + dimensionInfos[dimensionName] = dimensionInfo; + } + /** + * @readOnly + * @type {Array.} + */ + this.dimensions = dimensionNames; + + /** + * Infomation of each data dimension, like data type. + * @type {Object} + */ + this._dimensionInfos = dimensionInfos; + + /** + * @type {module:echarts/model/Model} + */ + this.hostModel = hostModel; + + /** + * @type {module:echarts/model/Model} + */ + this.dataType; + + /** + * Indices stores the indices of data subset after filtered. + * This data subset will be used in chart. + * @type {Array.} + * @readOnly + */ + this.indices = []; + + /** + * Data storage + * @type {Object.} + * @private + */ + this._storage = {}; + + /** + * @type {Array.} + */ + this._nameList = []; + /** + * @type {Array.} + */ + this._idList = []; + /** + * Models of data option is stored sparse for optimizing memory cost + * @type {Array.} + * @private + */ + this._optionModels = []; + + /** + * @param {module:echarts/data/List} + */ + this.stackedOn = null; + + /** + * Global visual properties after visual coding + * @type {Object} + * @private + */ + this._visual = {}; + + /** + * Globel layout properties. + * @type {Object} + * @private + */ + this._layout = {}; + + /** + * Item visual properties after visual coding + * @type {Array.} + * @private + */ + this._itemVisuals = []; + + /** + * Item layout properties after layout + * @type {Array.} + * @private + */ + this._itemLayouts = []; + + /** + * Graphic elemnents + * @type {Array.} + * @private + */ + this._graphicEls = []; + + /** + * @type {Array.} + * @private + */ + this._rawData; + + /** + * @type {Object} + * @private + */ + this._extent; + }; + + var listProto = List.prototype; + + listProto.type = 'list'; + /** + * If each data item has it's own option + * @type {boolean} + */ + listProto.hasItemOption = true; + + /** + * Get dimension name + * @param {string|number} dim + * Dimension can be concrete names like x, y, z, lng, lat, angle, radius + * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' + * @return {string} Concrete dim name. + */ + listProto.getDimension = function (dim) { + if (!isNaN(dim)) { + dim = this.dimensions[dim] || dim; + } + return dim; + }; + /** + * Get type and stackable info of particular dimension + * @param {string|number} dim + * Dimension can be concrete names like x, y, z, lng, lat, angle, radius + * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' + */ + listProto.getDimensionInfo = function (dim) { + return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]); + }; + + /** + * Initialize from data + * @param {Array.} data + * @param {Array.} [nameList] + * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number + */ + listProto.initData = function (data, nameList, dimValueGetter) { + data = data || []; + + if (true) { + if (!zrUtil.isArray(data)) { + throw new Error('Invalid data.'); + } + } + + this._rawData = data; + + // Clear + var storage = this._storage = {}; + var indices = this.indices = []; + + var dimensions = this.dimensions; + var size = data.length; + var dimensionInfoMap = this._dimensionInfos; + + var idList = []; + var nameRepeatCount = {}; + + nameList = nameList || []; + + // Init storage + for (var i = 0; i < dimensions.length; i++) { + var dimInfo = dimensionInfoMap[dimensions[i]]; + var DataCtor = dataCtors[dimInfo.type]; + storage[dimensions[i]] = new DataCtor(size); + } + + var self = this; + if (!dimValueGetter) { + self.hasItemOption = false; + } + // Default dim value getter + dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) { + var value = modelUtil.getDataItemValue(dataItem); + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(dataItem)) { + self.hasItemOption = true; + } + return modelUtil.converDataValue( + (value instanceof Array) + ? value[dimIndex] + // If value is a single number or something else not array. + : value, + dimensionInfoMap[dimName] + ); + }; + + for (var idx = 0; idx < data.length; idx++) { + var dataItem = data[idx]; + // Each data item is value + // [1, 2] + // 2 + // Bar chart, line chart which uses category axis + // only gives the 'y' value. 'x' value is the indices of cateogry + // Use a tempValue to normalize the value to be a (x, y) value + + // Store the data by dimensions + for (var k = 0; k < dimensions.length; k++) { + var dim = dimensions[k]; + var dimStorage = storage[dim]; + // PENDING NULL is empty or zero + dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k); + } + + indices.push(idx); + } + + // Use the name in option and create id + for (var i = 0; i < data.length; i++) { + if (!nameList[i]) { + if (data[i] && data[i].name != null) { + nameList[i] = data[i].name; + } + } + var name = nameList[i] || ''; + // Try using the id in option + var id = data[i] && data[i].id; + + if (!id && name) { + // Use name as id and add counter to avoid same name + nameRepeatCount[name] = nameRepeatCount[name] || 0; + id = name; + if (nameRepeatCount[name] > 0) { + id += '__ec__' + nameRepeatCount[name]; + } + nameRepeatCount[name]++; + } + id && (idList[i] = id); + } + + this._nameList = nameList; + this._idList = idList; + }; + + /** + * @return {number} + */ + listProto.count = function () { + return this.indices.length; + }; + + /** + * Get value. Return NaN if idx is out of range. + * @param {string} dim Dim must be concrete name. + * @param {number} idx + * @param {boolean} stack + * @return {number} + */ + listProto.get = function (dim, idx, stack) { + var storage = this._storage; + var dataIndex = this.indices[idx]; + + // If value not exists + if (dataIndex == null) { + return NaN; + } + + var value = storage[dim] && storage[dim][dataIndex]; + // FIXME ordinal data type is not stackable + if (stack) { + var dimensionInfo = this._dimensionInfos[dim]; + if (dimensionInfo && dimensionInfo.stackable) { + var stackedOn = this.stackedOn; + while (stackedOn) { + // Get no stacked data of stacked on + var stackedValue = stackedOn.get(dim, idx); + // Considering positive stack, negative stack and empty data + if ((value >= 0 && stackedValue > 0) // Positive stack + || (value <= 0 && stackedValue < 0) // Negative stack + ) { + value += stackedValue; + } + stackedOn = stackedOn.stackedOn; + } + } + } + return value; + }; + + /** + * Get value for multi dimensions. + * @param {Array.} [dimensions] If ignored, using all dimensions. + * @param {number} idx + * @param {boolean} stack + * @return {number} + */ + listProto.getValues = function (dimensions, idx, stack) { + var values = []; + + if (!zrUtil.isArray(dimensions)) { + stack = idx; + idx = dimensions; + dimensions = this.dimensions; + } + + for (var i = 0, len = dimensions.length; i < len; i++) { + values.push(this.get(dimensions[i], idx, stack)); + } + + return values; + }; + + /** + * If value is NaN. Inlcuding '-' + * @param {string} dim + * @param {number} idx + * @return {number} + */ + listProto.hasValue = function (idx) { + var dimensions = this.dimensions; + var dimensionInfos = this._dimensionInfos; + for (var i = 0, len = dimensions.length; i < len; i++) { + if ( + // Ordinal type can be string or number + dimensionInfos[dimensions[i]].type !== 'ordinal' + && isNaN(this.get(dimensions[i], idx)) + ) { + return false; + } + } + return true; + }; + + /** + * Get extent of data in one dimension + * @param {string} dim + * @param {boolean} stack + */ + listProto.getDataExtent = function (dim, stack) { + dim = this.getDimension(dim); + var dimData = this._storage[dim]; + var dimInfo = this.getDimensionInfo(dim); + stack = (dimInfo && dimInfo.stackable) && stack; + var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)]; + var value; + if (dimExtent) { + return dimExtent; + } + // var dimInfo = this._dimensionInfos[dim]; + if (dimData) { + var min = Infinity; + var max = -Infinity; + // var isOrdinal = dimInfo.type === 'ordinal'; + for (var i = 0, len = this.count(); i < len; i++) { + value = this.get(dim, i, stack); + // FIXME + // if (isOrdinal && typeof value === 'string') { + // value = zrUtil.indexOf(dimData, value); + // } + value < min && (min = value); + value > max && (max = value); + } + return (this._extent[dim + !!stack] = [min, max]); + } + else { + return [Infinity, -Infinity]; + } + }; + + /** + * Get sum of data in one dimension + * @param {string} dim + * @param {boolean} stack + */ + listProto.getSum = function (dim, stack) { + var dimData = this._storage[dim]; + var sum = 0; + if (dimData) { + for (var i = 0, len = this.count(); i < len; i++) { + var value = this.get(dim, i, stack); + if (!isNaN(value)) { + sum += value; + } + } + } + return sum; + }; + + /** + * Retreive the index with given value + * @param {number} idx + * @param {number} value + * @return {number} + */ + // FIXME Precision of float value + listProto.indexOf = function (dim, value) { + var storage = this._storage; + var dimData = storage[dim]; + var indices = this.indices; + + if (dimData) { + for (var i = 0, len = indices.length; i < len; i++) { + var rawIndex = indices[i]; + if (dimData[rawIndex] === value) { + return i; + } + } + } + return -1; + }; + + /** + * Retreive the index with given name + * @param {number} idx + * @param {number} name + * @return {number} + */ + listProto.indexOfName = function (name) { + var indices = this.indices; + var nameList = this._nameList; + + for (var i = 0, len = indices.length; i < len; i++) { + var rawIndex = indices[i]; + if (nameList[rawIndex] === name) { + return i; + } + } + + return -1; + }; + + /** + * Retreive the index with given raw data index + * @param {number} idx + * @param {number} name + * @return {number} + */ + listProto.indexOfRawIndex = function (rawIndex) { + // Indices are ascending + var indices = this.indices; + var left = 0; + var right = indices.length - 1; + while (left <= right) { + var mid = (left + right) / 2 | 0; + if (indices[mid] < rawIndex) { + left = mid + 1; + } + else if (indices[mid] > rawIndex) { + right = mid - 1; + } + else { + return mid; + } + } + return -1; + }; + + /** + * Retreive the index of nearest value + * @param {string} dim + * @param {number} value + * @param {boolean} stack If given value is after stacked + * @param {number} [maxDistance=Infinity] + * @return {number} + */ + listProto.indexOfNearest = function (dim, value, stack, maxDistance) { + var storage = this._storage; + var dimData = storage[dim]; + + if (maxDistance == null) { + maxDistance = Infinity; + } + + var nearestIdx = -1; + if (dimData) { + var minDist = Number.MAX_VALUE; + for (var i = 0, len = this.count(); i < len; i++) { + var diff = value - this.get(dim, i, stack); + var dist = Math.abs(diff); + if ( + diff <= maxDistance + && (dist < minDist + // For the case of two data are same on xAxis, which has sequence data. + // Show the nearest index + // https://github.com/ecomfe/echarts/issues/2869 + || (dist === minDist && diff > 0) + ) + ) { + minDist = dist; + nearestIdx = i; + } + } + } + return nearestIdx; + }; + + /** + * Get raw data index + * @param {number} idx + * @return {number} + */ + listProto.getRawIndex = function (idx) { + var rawIdx = this.indices[idx]; + return rawIdx == null ? -1 : rawIdx; + }; + + /** + * Get raw data item + * @param {number} idx + * @return {number} + */ + listProto.getRawDataItem = function (idx) { + return this._rawData[this.getRawIndex(idx)]; + }; + + /** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ + listProto.getName = function (idx) { + return this._nameList[this.indices[idx]] || ''; + }; + + /** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ + listProto.getId = function (idx) { + return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + ''); + }; + + + function normalizeDimensions(dimensions) { + if (!zrUtil.isArray(dimensions)) { + dimensions = [dimensions]; + } + return dimensions; + } + + /** + * Data iteration + * @param {string|Array.} + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * + * @example + * list.each('x', function (x, idx) {}); + * list.each(['x', 'y'], function (x, y, idx) {}); + * list.each(function (idx) {}) + */ + listProto.each = function (dims, cb, stack, context) { + if (typeof dims === 'function') { + context = stack; + stack = cb; + cb = dims; + dims = []; + } + + dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this); + + var value = []; + var dimSize = dims.length; + var indices = this.indices; + + context = context || this; + + for (var i = 0; i < indices.length; i++) { + // Simple optimization + switch (dimSize) { + case 0: + cb.call(context, i); + break; + case 1: + cb.call(context, this.get(dims[0], i, stack), i); + break; + case 2: + cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i); + break; + default: + for (var k = 0; k < dimSize; k++) { + value[k] = this.get(dims[k], i, stack); + } + // Index + value[k] = i; + cb.apply(context, value); + } + } + }; + + /** + * Data filter + * @param {string|Array.} + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + */ + listProto.filterSelf = function (dimensions, cb, stack, context) { + if (typeof dimensions === 'function') { + context = stack; + stack = cb; + cb = dimensions; + dimensions = []; + } + + dimensions = zrUtil.map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + var newIndices = []; + var value = []; + var dimSize = dimensions.length; + var indices = this.indices; + + context = context || this; + + for (var i = 0; i < indices.length; i++) { + var keep; + // Simple optimization + if (dimSize === 1) { + keep = cb.call( + context, this.get(dimensions[0], i, stack), i + ); + } + else { + for (var k = 0; k < dimSize; k++) { + value[k] = this.get(dimensions[k], i, stack); + } + value[k] = i; + keep = cb.apply(context, value); + } + if (keep) { + newIndices.push(indices[i]); + } + } + + this.indices = newIndices; + + // Reset data extent + this._extent = {}; + + return this; + }; + + /** + * Data mapping to a plain array + * @param {string|Array.} [dimensions] + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * @return {Array} + */ + listProto.mapArray = function (dimensions, cb, stack, context) { + if (typeof dimensions === 'function') { + context = stack; + stack = cb; + cb = dimensions; + dimensions = []; + } + + var result = []; + this.each(dimensions, function () { + result.push(cb && cb.apply(this, arguments)); + }, stack, context); + return result; + }; + + function cloneListForMapAndSample(original, excludeDimensions) { + var allDimensions = original.dimensions; + var list = new List( + zrUtil.map(allDimensions, original.getDimensionInfo, original), + original.hostModel + ); + // FIXME If needs stackedOn, value may already been stacked + transferProperties(list, original); + + var storage = list._storage = {}; + var originalStorage = original._storage; + // Init storage + for (var i = 0; i < allDimensions.length; i++) { + var dim = allDimensions[i]; + var dimStore = originalStorage[dim]; + if (zrUtil.indexOf(excludeDimensions, dim) >= 0) { + storage[dim] = new dimStore.constructor( + originalStorage[dim].length + ); + } + else { + // Direct reference for other dimensions + storage[dim] = originalStorage[dim]; + } + } + return list; + } + + /** + * Data mapping to a new List with given dimensions + * @param {string|Array.} dimensions + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * @return {Array} + */ + listProto.map = function (dimensions, cb, stack, context) { + dimensions = zrUtil.map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + var list = cloneListForMapAndSample(this, dimensions); + // Following properties are all immutable. + // So we can reference to the same value + var indices = list.indices = this.indices; + + var storage = list._storage; + + var tmpRetValue = []; + this.each(dimensions, function () { + var idx = arguments[arguments.length - 1]; + var retValue = cb && cb.apply(this, arguments); + if (retValue != null) { + // a number + if (typeof retValue === 'number') { + tmpRetValue[0] = retValue; + retValue = tmpRetValue; + } + for (var i = 0; i < retValue.length; i++) { + var dim = dimensions[i]; + var dimStore = storage[dim]; + var rawIdx = indices[idx]; + if (dimStore) { + dimStore[rawIdx] = retValue[i]; + } + } + } + }, stack, context); + + return list; + }; + + /** + * Large data down sampling on given dimension + * @param {string} dimension + * @param {number} rate + * @param {Function} sampleValue + * @param {Function} sampleIndex Sample index for name and id + */ + listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) { + var list = cloneListForMapAndSample(this, [dimension]); + var storage = this._storage; + var targetStorage = list._storage; + + var originalIndices = this.indices; + var indices = list.indices = []; + + var frameValues = []; + var frameIndices = []; + var frameSize = Math.floor(1 / rate); + + var dimStore = targetStorage[dimension]; + var len = this.count(); + // Copy data from original data + for (var i = 0; i < storage[dimension].length; i++) { + targetStorage[dimension][i] = storage[dimension][i]; + } + for (var i = 0; i < len; i += frameSize) { + // Last frame + if (frameSize > len - i) { + frameSize = len - i; + frameValues.length = frameSize; + } + for (var k = 0; k < frameSize; k++) { + var idx = originalIndices[i + k]; + frameValues[k] = dimStore[idx]; + frameIndices[k] = idx; + } + var value = sampleValue(frameValues); + var idx = frameIndices[sampleIndex(frameValues, value) || 0]; + // Only write value on the filtered data + dimStore[idx] = value; + indices.push(idx); + } + + return list; + }; + + /** + * Get model of one data item. + * + * @param {number} idx + */ + // FIXME Model proxy ? + listProto.getItemModel = function (idx) { + var hostModel = this.hostModel; + idx = this.indices[idx]; + return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel); + }; + + /** + * Create a data differ + * @param {module:echarts/data/List} otherList + * @return {module:echarts/data/DataDiffer} + */ + listProto.diff = function (otherList) { + var idList = this._idList; + var otherIdList = otherList && otherList._idList; + return new DataDiffer( + otherList ? otherList.indices : [], this.indices, function (idx) { + return otherIdList[idx] || (idx + ''); + }, function (idx) { + return idList[idx] || (idx + ''); + } + ); + }; + /** + * Get visual property. + * @param {string} key + */ + listProto.getVisual = function (key) { + var visual = this._visual; + return visual && visual[key]; + }; + + /** + * Set visual property + * @param {string|Object} key + * @param {*} [value] + * + * @example + * setVisual('color', color); + * setVisual({ + * 'color': color + * }); + */ + listProto.setVisual = function (key, val) { + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.setVisual(name, key[name]); + } + } + return; + } + this._visual = this._visual || {}; + this._visual[key] = val; + }; + + /** + * Set layout property. + * @param {string} key + * @param {*} [val] + */ + listProto.setLayout = function (key, val) { + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.setLayout(name, key[name]); + } + } + return; + } + this._layout[key] = val; + }; + + /** + * Get layout property. + * @param {string} key. + * @return {*} + */ + listProto.getLayout = function (key) { + return this._layout[key]; + }; + + /** + * Get layout of single data item + * @param {number} idx + */ + listProto.getItemLayout = function (idx) { + return this._itemLayouts[idx]; + }; + + /** + * Set layout of single data item + * @param {number} idx + * @param {Object} layout + * @param {boolean=} [merge=false] + */ + listProto.setItemLayout = function (idx, layout, merge) { + this._itemLayouts[idx] = merge + ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) + : layout; + }; + + /** + * Clear all layout of single data item + */ + listProto.clearItemLayouts = function () { + this._itemLayouts.length = 0; + }; + + /** + * Get visual property of single data item + * @param {number} idx + * @param {string} key + * @param {boolean} ignoreParent + */ + listProto.getItemVisual = function (idx, key, ignoreParent) { + var itemVisual = this._itemVisuals[idx]; + var val = itemVisual && itemVisual[key]; + if (val == null && !ignoreParent) { + // Use global visual property + return this.getVisual(key); + } + return val; + }; + + /** + * Set visual property of single data item + * + * @param {number} idx + * @param {string|Object} key + * @param {*} [value] + * + * @example + * setItemVisual(0, 'color', color); + * setItemVisual(0, { + * 'color': color + * }); + */ + listProto.setItemVisual = function (idx, key, value) { + var itemVisual = this._itemVisuals[idx] || {}; + this._itemVisuals[idx] = itemVisual; + + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + itemVisual[name] = key[name]; + } + } + return; + } + itemVisual[key] = value; + }; + + /** + * Clear itemVisuals and list visual. + */ + listProto.clearAllVisual = function () { + this._visual = {}; + this._itemVisuals = []; + }; + + var setItemDataAndSeriesIndex = function (child) { + child.seriesIndex = this.seriesIndex; + child.dataIndex = this.dataIndex; + child.dataType = this.dataType; + }; + /** + * Set graphic element relative to data. It can be set as null + * @param {number} idx + * @param {module:zrender/Element} [el] + */ + listProto.setItemGraphicEl = function (idx, el) { + var hostModel = this.hostModel; + + if (el) { + // Add data index and series index for indexing the data by element + // Useful in tooltip + el.dataIndex = idx; + el.dataType = this.dataType; + el.seriesIndex = hostModel && hostModel.seriesIndex; + if (el.type === 'group') { + el.traverse(setItemDataAndSeriesIndex, el); + } + } + + this._graphicEls[idx] = el; + }; + + /** + * @param {number} idx + * @return {module:zrender/Element} + */ + listProto.getItemGraphicEl = function (idx) { + return this._graphicEls[idx]; + }; + + /** + * @param {Function} cb + * @param {*} context + */ + listProto.eachItemGraphicEl = function (cb, context) { + zrUtil.each(this._graphicEls, function (el, idx) { + if (el) { + cb && cb.call(context, el, idx); + } + }); + }; + + /** + * Shallow clone a new list except visual and layout properties, and graph elements. + * New list only change the indices. + */ + listProto.cloneShallow = function () { + var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this); + var list = new List(dimensionInfoList, this.hostModel); + + // FIXME + list._storage = this._storage; + + transferProperties(list, this); + + + // Clone will not change the data extent and indices + list.indices = this.indices.slice(); + + if (this._extent) { + list._extent = zrUtil.extend({}, this._extent); + } + + return list; + }; + + /** + * Wrap some method to add more feature + * @param {string} methodName + * @param {Function} injectFunction + */ + listProto.wrapMethod = function (methodName, injectFunction) { + var originalMethod = this[methodName]; + if (typeof originalMethod !== 'function') { + return; + } + this.__wrappedMethods = this.__wrappedMethods || []; + this.__wrappedMethods.push(methodName); + this[methodName] = function () { + var res = originalMethod.apply(this, arguments); + return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments))); + }; + }; + + // Methods that create a new list based on this list should be listed here. + // Notice that those method should `RETURN` the new list. + listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; + // Methods that change indices of this list should be listed here. + listProto.CHANGABLE_METHODS = ['filterSelf']; + + module.exports = List; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 98 */ +/***/ function(module, exports) { + + 'use strict'; + + + function defaultKeyGetter(item) { + return item; + } + + function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter) { + this._old = oldArr; + this._new = newArr; + + this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; + this._newKeyGetter = newKeyGetter || defaultKeyGetter; + } + + DataDiffer.prototype = { + + constructor: DataDiffer, + + /** + * Callback function when add a data + */ + add: function (func) { + this._add = func; + return this; + }, + + /** + * Callback function when update a data + */ + update: function (func) { + this._update = func; + return this; + }, + + /** + * Callback function when remove a data + */ + remove: function (func) { + this._remove = func; + return this; + }, + + execute: function () { + var oldArr = this._old; + var newArr = this._new; + var oldKeyGetter = this._oldKeyGetter; + var newKeyGetter = this._newKeyGetter; + + var oldDataIndexMap = {}; + var newDataIndexMap = {}; + var oldDataKeyArr = []; + var newDataKeyArr = []; + var i; + + initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, oldKeyGetter); + initIndexMap(newArr, newDataIndexMap, newDataKeyArr, newKeyGetter); + + // Travel by inverted order to make sure order consistency + // when duplicate keys exists (consider newDataIndex.pop() below). + // For performance consideration, these code below do not look neat. + for (i = 0; i < oldArr.length; i++) { + var key = oldDataKeyArr[i]; + var idx = newDataIndexMap[key]; + + // idx can never be empty array here. see 'set null' logic below. + if (idx != null) { + // Consider there is duplicate key (for example, use dataItem.name as key). + // We should make sure every item in newArr and oldArr can be visited. + var len = idx.length; + if (len) { + len === 1 && (newDataIndexMap[key] = null); + idx = idx.unshift(); + } + else { + newDataIndexMap[key] = null; + } + this._update && this._update(idx, i); + } + else { + this._remove && this._remove(i); + } + } + + for (var i = 0; i < newDataKeyArr.length; i++) { + var key = newDataKeyArr[i]; + if (newDataIndexMap.hasOwnProperty(key)) { + var idx = newDataIndexMap[key]; + if (idx == null) { + continue; + } + // idx can never be empty array here. see 'set null' logic above. + if (!idx.length) { + this._add && this._add(idx); + } + else { + for (var j = 0, len = idx.length; j < len; j++) { + this._add && this._add(idx[j]); + } + } + } + } + } + }; + + function initIndexMap(arr, map, keyArr, keyGetter) { + for (var i = 0; i < arr.length; i++) { + var key = keyGetter(arr[i], i); + var existence = map[key]; + if (existence == null) { + keyArr.push(key); + map[key] = i; + } + else { + if (!existence.length) { + map[key] = existence = [existence]; + } + existence.push(i); + } + } + } + + module.exports = DataDiffer; + + +/***/ }, +/* 99 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + var PRIORITY = echarts.PRIORITY; + + __webpack_require__(100); + __webpack_require__(103); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'line', 'circle', 'line' + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'line' + )); + + // Down sample after filter + echarts.registerProcessor(PRIORITY.PROCESSOR.STATISTIC, zrUtil.curry( + __webpack_require__(111), 'line' + )); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 100 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.line', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + if (true) { + var coordSys = option.coordinateSystem; + if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { + throw new Error('Line not support coordinateSystem besides cartesian and polar'); + } + } + return createListFromArray(option.data, this, ecModel); + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + // stack: null + // xAxisIndex: 0, + // yAxisIndex: 0, + + // polarIndex: 0, + + // If clip the overflow value + clipOverflow: true, + + label: { + normal: { + position: 'top' + } + }, + // itemStyle: { + // normal: {}, + // emphasis: {} + // }, + lineStyle: { + normal: { + width: 2, + type: 'solid' + } + }, + // areaStyle: {}, + // false, 'start', 'end', 'middle' + step: false, + + // Disabled if step is true + smooth: false, + smoothMonotone: null, + // 拐点图形类型 + symbol: 'emptyCircle', + // 拐点图形大小 + symbolSize: 4, + // 拐点图形旋转控制 + symbolRotate: null, + + // 是否显示 symbol, 只有在 tooltip hover 的时候显示 + showSymbol: true, + // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略) + showAllSymbol: false, + + // 是否连接断点 + connectNulls: false, + + // 数据过滤,'average', 'max', 'min', 'sum' + sampling: 'none', + + animationEasing: 'linear', + + // Disable progressive + progressive: 0, + hoverLayerThreshold: Infinity + } + }); + + +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var CoordinateSystem = __webpack_require__(26); + var getDataItemValue = modelUtil.getDataItemValue; + var converDataValue = modelUtil.converDataValue; + + function firstDataNotNull(data) { + var i = 0; + while (i < data.length && data[i] == null) { + i++; + } + return data[i]; + } + function ifNeedCompleteOrdinalData(data) { + var sampleItem = firstDataNotNull(data); + return sampleItem != null + && !zrUtil.isArray(getDataItemValue(sampleItem)); + } + + /** + * Helper function to create a list from option data + */ + function createListFromArray(data, seriesModel, ecModel) { + // If data is undefined + data = data || []; + + if (true) { + if (!zrUtil.isArray(data)) { + throw new Error('Invalid data.'); + } + } + + var coordSysName = seriesModel.get('coordinateSystem'); + var creator = creators[coordSysName]; + var registeredCoordSys = CoordinateSystem.get(coordSysName); + // FIXME + var axesInfo = creator && creator(data, seriesModel, ecModel); + var dimensions = axesInfo && axesInfo.dimensions; + if (!dimensions) { + // Get dimensions from registered coordinate system + dimensions = (registeredCoordSys && registeredCoordSys.dimensions) || ['x', 'y']; + dimensions = completeDimensions(dimensions, data, dimensions.concat(['value'])); + } + var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1; + + var list = new List(dimensions, seriesModel); + + var nameList = createNameList(axesInfo, data); + + var categories = {}; + var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data)) + ? function (itemOpt, dimName, dataIndex, dimIndex) { + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(itemOpt)) { + list.hasItemOption = true; + } + + // Use dataIndex as ordinal value in categoryAxis + return dimIndex === categoryIndex + ? dataIndex + : converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]); + } + : function (itemOpt, dimName, dataIndex, dimIndex) { + var value = getDataItemValue(itemOpt); + var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]); + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(itemOpt)) { + list.hasItemOption = true; + } + + var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels; + if (categoryAxesModels && categoryAxesModels[dimName]) { + // If given value is a category string + if (typeof val === 'string') { + // Lazy get categories + categories[dimName] = categories[dimName] + || categoryAxesModels[dimName].getCategories(); + val = zrUtil.indexOf(categories[dimName], val); + if (val < 0 && !isNaN(val)) { + // In case some one write '1', '2' istead of 1, 2 + val = +val; + } + } + } + return val; + }; + + list.hasItemOption = false; + list.initData(data, nameList, dimValueGetter); + + return list; + } + + function isStackable(axisType) { + return axisType !== 'category' && axisType !== 'time'; + } + + function getDimTypeByAxis(axisType) { + return axisType === 'category' + ? 'ordinal' + : axisType === 'time' + ? 'time' + : 'float'; + } + + /** + * Creaters for each coord system. + */ + var creators = { + + cartesian2d: function (data, seriesModel, ecModel) { + + var axesModels = zrUtil.map(['xAxis', 'yAxis'], function (name) { + return ecModel.queryComponents({ + mainType: name, + index: seriesModel.get(name + 'Index'), + id: seriesModel.get(name + 'Id') + })[0]; + }); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + if (true) { + if (!xAxisModel) { + throw new Error('xAxis "' + zrUtil.retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('xAxisId'), + 0 + ) + '" not found'); + } + if (!yAxisModel) { + throw new Error('yAxis "' + zrUtil.retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('yAxisId'), + 0 + ) + '" not found'); + } + } + + var xAxisType = xAxisModel.get('type'); + var yAxisType = yAxisModel.get('type'); + + var dimensions = [ + { + name: 'x', + type: getDimTypeByAxis(xAxisType), + stackable: isStackable(xAxisType) + }, + { + name: 'y', + // If two category axes + type: getDimTypeByAxis(yAxisType), + stackable: isStackable(yAxisType) + } + ]; + + var isXAxisCateogry = xAxisType === 'category'; + var isYAxisCategory = yAxisType === 'category'; + + completeDimensions(dimensions, data, ['x', 'y', 'z']); + + var categoryAxesModels = {}; + if (isXAxisCateogry) { + categoryAxesModels.x = xAxisModel; + } + if (isYAxisCategory) { + categoryAxesModels.y = yAxisModel; + } + return { + dimensions: dimensions, + categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1), + categoryAxesModels: categoryAxesModels + }; + }, + + polar: function (data, seriesModel, ecModel) { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: seriesModel.get('polarIndex'), + id: seriesModel.get('polarId') + })[0]; + + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + + if (true) { + if (!angleAxisModel) { + throw new Error('angleAxis option not found'); + } + if (!radiusAxisModel) { + throw new Error('radiusAxis option not found'); + } + } + + var radiusAxisType = radiusAxisModel.get('type'); + var angleAxisType = angleAxisModel.get('type'); + + var dimensions = [ + { + name: 'radius', + type: getDimTypeByAxis(radiusAxisType), + stackable: isStackable(radiusAxisType) + }, + { + name: 'angle', + type: getDimTypeByAxis(angleAxisType), + stackable: isStackable(angleAxisType) + } + ]; + var isAngleAxisCateogry = angleAxisType === 'category'; + var isRadiusAxisCateogry = radiusAxisType === 'category'; + + completeDimensions(dimensions, data, ['radius', 'angle', 'value']); + + var categoryAxesModels = {}; + if (isRadiusAxisCateogry) { + categoryAxesModels.radius = radiusAxisModel; + } + if (isAngleAxisCateogry) { + categoryAxesModels.angle = angleAxisModel; + } + return { + dimensions: dimensions, + categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1), + categoryAxesModels: categoryAxesModels + }; + }, + + geo: function (data, seriesModel, ecModel) { + // TODO Region + // 多个散点图系列在同一个地区的时候 + return { + dimensions: completeDimensions([ + {name: 'lng'}, + {name: 'lat'} + ], data, ['lng', 'lat', 'value']) + }; + } + }; + + function createNameList(result, data) { + var nameList = []; + + var categoryDim = result && result.dimensions[result.categoryIndex]; + var categoryAxisModel; + if (categoryDim) { + categoryAxisModel = result.categoryAxesModels[categoryDim.name]; + } + + if (categoryAxisModel) { + // FIXME Two category axis + var categories = categoryAxisModel.getCategories(); + if (categories) { + var dataLen = data.length; + // Ordered data is given explicitly like + // [[3, 0.2], [1, 0.3], [2, 0.15]] + // or given scatter data, + // pick the category + if (zrUtil.isArray(data[0]) && data[0].length > 1) { + nameList = []; + for (var i = 0; i < dataLen; i++) { + nameList[i] = categories[data[i][result.categoryIndex || 0]]; + } + } + else { + nameList = categories.slice(0); + } + } + } + + return nameList; + } + + module.exports = createListFromArray; + + + +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Complete dimensions by data (guess dimension). + */ + + + var zrUtil = __webpack_require__(4); + + /** + * Complete the dimensions array guessed from the data structure. + * @param {Array.} dimensions Necessary dimensions, like ['x', 'y'] + * @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]] + * @param {Array.} defaultNames Default names to fill not necessary dimensions, like ['value'] + * @param {string} extraPrefix Prefix of name when filling the left dimensions. + * @return {Array.} + */ + function completeDimensions(dimensions, data, defaultNames, extraPrefix) { + if (!data) { + return dimensions; + } + + var value0 = retrieveValue(data[0]); + var dimSize = zrUtil.isArray(value0) && value0.length || 1; + + defaultNames = defaultNames || []; + extraPrefix = extraPrefix || 'extra'; + for (var i = 0; i < dimSize; i++) { + if (!dimensions[i]) { + var name = defaultNames[i] || (extraPrefix + (i - defaultNames.length)); + dimensions[i] = guessOrdinal(data, i) + ? {type: 'ordinal', name: name} + : name; + } + } + + return dimensions; + } + + // The rule should not be complex, otherwise user might not + // be able to known where the data is wrong. + var guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) { + for (var i = 0, len = data.length; i < len; i++) { + var value = retrieveValue(data[i]); + + if (!zrUtil.isArray(value)) { + return false; + } + + var value = value[dimIndex]; + if (value != null && isFinite(value)) { + return false; + } + else if (zrUtil.isString(value) && value !== '-') { + return true; + } + } + return false; + }; + + function retrieveValue(o) { + return zrUtil.isArray(o) ? o : zrUtil.isObject(o) ? o.value: o; + } + + module.exports = completeDimensions; + + + +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // FIXME step not support polar + + + var zrUtil = __webpack_require__(4); + var SymbolDraw = __webpack_require__(104); + var Symbol = __webpack_require__(105); + var lineAnimationDiff = __webpack_require__(107); + var graphic = __webpack_require__(43); + + var polyHelper = __webpack_require__(108); + + var ChartView = __webpack_require__(42); + + function isPointsSame(points1, points2) { + if (points1.length !== points2.length) { + return; + } + for (var i = 0; i < points1.length; i++) { + var p1 = points1[i]; + var p2 = points2[i]; + if (p1[0] !== p2[0] || p1[1] !== p2[1]) { + return; + } + } + return true; + } + + function getSmooth(smooth) { + return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0); + } + + function getAxisExtentWithGap(axis) { + var extent = axis.getGlobalExtent(); + if (axis.onBand) { + // Remove extra 1px to avoid line miter in clipped edge + var halfBandWidth = axis.getBandWidth() / 2 - 1; + var dir = extent[1] > extent[0] ? 1 : -1; + extent[0] += dir * halfBandWidth; + extent[1] -= dir * halfBandWidth; + } + return extent; + } + + function sign(val) { + return val >= 0 ? 1 : -1; + } + /** + * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys + * @param {module:echarts/data/List} data + * @param {Array.>} points + * @private + */ + function getStackedOnPoints(coordSys, data) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = baseAxis.onZero + ? 0 : valueAxis.scale.getExtent()[0]; + + var valueDim = valueAxis.dim; + + var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0; + + return data.mapArray([valueDim], function (val, idx) { + var stackedOnSameSign; + var stackedOn = data.stackedOn; + // Find first stacked value with same sign + while (stackedOn && + sign(stackedOn.get(valueDim, idx)) === sign(val) + ) { + stackedOnSameSign = stackedOn; + break; + } + var stackedData = []; + stackedData[baseDataOffset] = data.get(baseAxis.dim, idx); + stackedData[1 - baseDataOffset] = stackedOnSameSign + ? stackedOnSameSign.get(valueDim, idx, true) : valueStart; + + return coordSys.dataToPoint(stackedData); + }, true); + } + + function queryDataIndex(data, payload) { + if (payload.dataIndex != null) { + return payload.dataIndex; + } + else if (payload.name != null) { + return data.indexOfName(payload.name); + } + } + + function createGridClipShape(cartesian, hasAnimation, seriesModel) { + var xExtent = getAxisExtentWithGap(cartesian.getAxis('x')); + var yExtent = getAxisExtentWithGap(cartesian.getAxis('y')); + var isHorizontal = cartesian.getBaseAxis().isHorizontal(); + + var x = Math.min(xExtent[0], xExtent[1]); + var y = Math.min(yExtent[0], yExtent[1]); + var width = Math.max(xExtent[0], xExtent[1]) - x; + var height = Math.max(yExtent[0], yExtent[1]) - y; + var lineWidth = seriesModel.get('lineStyle.normal.width') || 2; + // Expand clip shape to avoid clipping when line value exceeds axis + var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height); + if (isHorizontal) { + y -= expandSize; + height += expandSize * 2; + } + else { + x -= expandSize; + width += expandSize * 2; + } + + var clipPath = new graphic.Rect({ + shape: { + x: x, + y: y, + width: width, + height: height + } + }); + + if (hasAnimation) { + clipPath.shape[isHorizontal ? 'width' : 'height'] = 0; + graphic.initProps(clipPath, { + shape: { + width: width, + height: height + } + }, seriesModel); + } + + return clipPath; + } + + function createPolarClipShape(polar, hasAnimation, seriesModel) { + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + + var radiusExtent = radiusAxis.getExtent(); + var angleExtent = angleAxis.getExtent(); + + var RADIAN = Math.PI / 180; + + var clipPath = new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: radiusExtent[0], + r: radiusExtent[1], + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse + } + }); + + if (hasAnimation) { + clipPath.shape.endAngle = -angleExtent[0] * RADIAN; + graphic.initProps(clipPath, { + shape: { + endAngle: -angleExtent[1] * RADIAN + } + }, seriesModel); + } + + return clipPath; + } + + function createClipShape(coordSys, hasAnimation, seriesModel) { + return coordSys.type === 'polar' + ? createPolarClipShape(coordSys, hasAnimation, seriesModel) + : createGridClipShape(coordSys, hasAnimation, seriesModel); + } + + function turnPointsIntoStep(points, coordSys, stepTurnAt) { + var baseAxis = coordSys.getBaseAxis(); + var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; + + var stepPoints = []; + for (var i = 0; i < points.length - 1; i++) { + var nextPt = points[i + 1]; + var pt = points[i]; + stepPoints.push(pt); + + var stepPt = []; + switch (stepTurnAt) { + case 'end': + stepPt[baseIndex] = nextPt[baseIndex]; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + // default is start + stepPoints.push(stepPt); + break; + case 'middle': + // default is start + var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2; + var stepPt2 = []; + stepPt[baseIndex] = stepPt2[baseIndex] = middle; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]; + stepPoints.push(stepPt); + stepPoints.push(stepPt2); + break; + default: + stepPt[baseIndex] = pt[baseIndex]; + stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; + // default is start + stepPoints.push(stepPt); + } + } + // Last points + points[i] && stepPoints.push(points[i]); + return stepPoints; + } + + function clamp(number, extent) { + return Math.max(Math.min(number, extent[1]), extent[0]); + } + + function getVisualGradient(data, coordSys) { + var visualMetaList = data.getVisual('visualMeta'); + if (!visualMetaList || !visualMetaList.length) { + return; + } + + var visualMeta; + for (var i = visualMetaList.length - 1; i >= 0; i--) { + // Can only be x or y + if (visualMetaList[i].dimension < 2) { + visualMeta = visualMetaList[i]; + break; + } + } + if (!visualMeta || coordSys.type !== 'cartesian2d') { + if (true) { + console.warn('Visual map on line style only support x or y dimension.'); + } + return; + } + var dimension = visualMeta.dimension; + var dimName = data.dimensions[dimension]; + var dataExtent = data.getDataExtent(dimName); + + var stops = visualMeta.stops; + + var colorStops = []; + if (stops[0].interval) { + stops.sort(function (a, b) { + return a.interval[0] - b.interval[0]; + }); + } + + var firstStop = stops[0]; + var lastStop = stops[stops.length - 1]; + // Interval can be infinity in piecewise case + var min = firstStop.interval ? clamp(firstStop.interval[0], dataExtent) : firstStop.value; + var max = lastStop.interval ? clamp(lastStop.interval[1], dataExtent) : lastStop.value; + var stopsSpan = max - min; + + // In the piecewise case data out of visual range + // ----dataMin----dataMax-----visualMin----visualMax + if (stopsSpan === 0) { + return data.getItemVisual(0, 'color'); + } + for (var i = 0; i < stops.length; i++) { + // Piecewise + if (stops[i].interval) { + if (stops[i].interval[1] === stops[i].interval[0]) { + continue; + } + colorStops.push({ + // Make sure offset is between 0 and 1 + offset: (clamp(stops[i].interval[0], dataExtent) - min) / stopsSpan, + color: stops[i].color + }, { + offset: (clamp(stops[i].interval[1], dataExtent) - min) / stopsSpan, + color: stops[i].color + }); + } + // Continous + else { + // if (i > 0 && stops[i].value === stops[i - 1].value) { + // continue; + // } + colorStops.push({ + offset: (stops[i].value - min) / stopsSpan, + color: stops[i].color + }); + } + } + var gradient = new graphic.LinearGradient( + 0, 0, 0, 0, colorStops, true + ); + var axis = coordSys.getAxis(dimName); + + var start = Math.round(axis.toGlobalCoord(axis.dataToCoord(min))); + var end = Math.round(axis.toGlobalCoord(axis.dataToCoord(max))); + // zrUtil.each(colorStops, function (colorStop) { + // // Make sure each offset has rounded px to avoid not sharp edge + // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start); + // }); + + gradient[dimName] = start; + gradient[dimName + '2'] = end; + + return gradient; + } + + module.exports = ChartView.extend({ + + type: 'line', + + init: function () { + var lineGroup = new graphic.Group(); + + var symbolDraw = new SymbolDraw(); + this.group.add(symbolDraw.group); + + this._symbolDraw = symbolDraw; + this._lineGroup = lineGroup; + }, + + render: function (seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var group = this.group; + var data = seriesModel.getData(); + var lineStyleModel = seriesModel.getModel('lineStyle.normal'); + var areaStyleModel = seriesModel.getModel('areaStyle.normal'); + + var points = data.mapArray(data.getItemLayout, true); + + var isCoordSysPolar = coordSys.type === 'polar'; + var prevCoordSys = this._coordSys; + + var symbolDraw = this._symbolDraw; + var polyline = this._polyline; + var polygon = this._polygon; + + var lineGroup = this._lineGroup; + + var hasAnimation = seriesModel.get('animation'); + + var isAreaChart = !areaStyleModel.isEmpty(); + var stackedOnPoints = getStackedOnPoints(coordSys, data); + + var showSymbol = seriesModel.get('showSymbol'); + + var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol') + && this._getSymbolIgnoreFunc(data, coordSys); + + // Remove temporary symbols + var oldData = this._data; + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + + // Remove previous created symbols if showSymbol changed to false + if (!showSymbol) { + symbolDraw.remove(); + } + + group.add(lineGroup); + + // FIXME step not support polar + var step = !isCoordSysPolar && seriesModel.get('step'); + // Initialization animation or coordinate system changed + if ( + !(polyline && prevCoordSys.type === coordSys.type && step === this._step) + ) { + showSymbol && symbolDraw.updateData(data, isSymbolIgnore); + + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step); + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); + } + + polyline = this._newPolyline(points, coordSys, hasAnimation); + if (isAreaChart) { + polygon = this._newPolygon( + points, stackedOnPoints, + coordSys, hasAnimation + ); + } + lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel)); + } + else { + if (isAreaChart && !polygon) { + // If areaStyle is added + polygon = this._newPolygon( + points, stackedOnPoints, + coordSys, hasAnimation + ); + } + else if (polygon && !isAreaChart) { + // If areaStyle is removed + lineGroup.remove(polygon); + polygon = this._polygon = null; + } + + // Update clipPath + lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel)); + + // Always update, or it is wrong in the case turning on legend + // because points are not changed + showSymbol && symbolDraw.updateData(data, isSymbolIgnore); + + // Stop symbol animation and sync with line points + // FIXME performance? + data.eachItemGraphicEl(function (el) { + el.stopAnimation(true); + }); + + // In the case data zoom triggerred refreshing frequently + // Data may not change if line has a category axis. So it should animate nothing + if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) + || !isPointsSame(this._points, points) + ) { + if (hasAnimation) { + this._updateAnimation( + data, stackedOnPoints, coordSys, api, step + ); + } + else { + // Not do it in update with animation + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step); + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); + } + + polyline.setShape({ + points: points + }); + polygon && polygon.setShape({ + points: points, + stackedOnPoints: stackedOnPoints + }); + } + } + } + + var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color'); + polyline.useStyle(zrUtil.defaults( + // Use color in lineStyle first + lineStyleModel.getLineStyle(), + { + fill: 'none', + stroke: visualColor, + lineJoin: 'bevel' + } + )); + + var smooth = seriesModel.get('smooth'); + smooth = getSmooth(seriesModel.get('smooth')); + polyline.setShape({ + smooth: smooth, + smoothMonotone: seriesModel.get('smoothMonotone'), + connectNulls: seriesModel.get('connectNulls') + }); + + if (polygon) { + var stackedOn = data.stackedOn; + var stackedOnSmooth = 0; + + polygon.useStyle(zrUtil.defaults( + areaStyleModel.getAreaStyle(), + { + fill: visualColor, + opacity: 0.7, + lineJoin: 'bevel' + } + )); + + if (stackedOn) { + var stackedOnSeries = stackedOn.hostModel; + stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth')); + } + + polygon.setShape({ + smooth: smooth, + stackedOnSmooth: stackedOnSmooth, + smoothMonotone: seriesModel.get('smoothMonotone'), + connectNulls: seriesModel.get('connectNulls') + }); + } + + this._data = data; + // Save the coordinate system for transition animation when data changed + this._coordSys = coordSys; + this._stackedOnPoints = stackedOnPoints; + this._points = points; + this._step = step; + }, + + highlight: function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + + if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) { + var symbol = data.getItemGraphicEl(dataIndex); + if (!symbol) { + // Create a temporary symbol if it is not exists + var pt = data.getItemLayout(dataIndex); + symbol = new Symbol(data, dataIndex); + symbol.position = pt; + symbol.setZ( + seriesModel.get('zlevel'), + seriesModel.get('z') + ); + symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]); + symbol.__temp = true; + data.setItemGraphicEl(dataIndex, symbol); + + // Stop scale animation + symbol.stopSymbolAnimation(true); + + this.group.add(symbol); + } + symbol.highlight(); + } + else { + // Highlight whole series + ChartView.prototype.highlight.call( + this, seriesModel, ecModel, api, payload + ); + } + }, + + downplay: function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + if (dataIndex != null && dataIndex >= 0) { + var symbol = data.getItemGraphicEl(dataIndex); + if (symbol) { + if (symbol.__temp) { + data.setItemGraphicEl(dataIndex, null); + this.group.remove(symbol); + } + else { + symbol.downplay(); + } + } + } + else { + // Downplay whole series + ChartView.prototype.downplay.call( + this, seriesModel, ecModel, api, payload + ); + } + }, + + /** + * @param {module:zrender/container/Group} group + * @param {Array.>} points + * @private + */ + _newPolyline: function (points) { + var polyline = this._polyline; + // Remove previous created polyline + if (polyline) { + this._lineGroup.remove(polyline); + } + + polyline = new polyHelper.Polyline({ + shape: { + points: points + }, + silent: true, + z2: 10 + }); + + this._lineGroup.add(polyline); + + this._polyline = polyline; + + return polyline; + }, + + /** + * @param {module:zrender/container/Group} group + * @param {Array.>} stackedOnPoints + * @param {Array.>} points + * @private + */ + _newPolygon: function (points, stackedOnPoints) { + var polygon = this._polygon; + // Remove previous created polygon + if (polygon) { + this._lineGroup.remove(polygon); + } + + polygon = new polyHelper.Polygon({ + shape: { + points: points, + stackedOnPoints: stackedOnPoints + }, + silent: true + }); + + this._lineGroup.add(polygon); + + this._polygon = polygon; + return polygon; + }, + /** + * @private + */ + _getSymbolIgnoreFunc: function (data, coordSys) { + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + // `getLabelInterval` is provided by echarts/component/axis + if (categoryAxis && categoryAxis.isLabelIgnored) { + return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis); + } + }, + + /** + * @private + */ + // FIXME Two value axis + _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) { + var polyline = this._polyline; + var polygon = this._polygon; + var seriesModel = data.hostModel; + + var diff = lineAnimationDiff( + this._data, data, + this._stackedOnPoints, stackedOnPoints, + this._coordSys, coordSys + ); + + var current = diff.current; + var stackedOnCurrent = diff.stackedOnCurrent; + var next = diff.next; + var stackedOnNext = diff.stackedOnNext; + if (step) { + // TODO If stacked series is not step + current = turnPointsIntoStep(diff.current, coordSys, step); + stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step); + next = turnPointsIntoStep(diff.next, coordSys, step); + stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step); + } + polyline.shape.__points = diff.current; + polyline.shape.points = current; + + graphic.updateProps(polyline, { + shape: { + points: next + } + }, seriesModel); + + if (polygon) { + polygon.setShape({ + points: current, + stackedOnPoints: stackedOnCurrent + }); + graphic.updateProps(polygon, { + shape: { + points: next, + stackedOnPoints: stackedOnNext, + __points: diff.next + } + }, seriesModel); + } + + var updatedDataInfo = []; + var diffStatus = diff.status; + + for (var i = 0; i < diffStatus.length; i++) { + var cmd = diffStatus[i].cmd; + if (cmd === '=') { + var el = data.getItemGraphicEl(diffStatus[i].idx1); + if (el) { + updatedDataInfo.push({ + el: el, + ptIdx: i // Index of points + }); + } + } + } + + if (polyline.animators && polyline.animators.length) { + polyline.animators[0].during(function () { + for (var i = 0; i < updatedDataInfo.length; i++) { + var el = updatedDataInfo[i].el; + el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]); + } + }); + } + }, + + remove: function (ecModel) { + var group = this.group; + var oldData = this._data; + this._lineGroup.removeAll(); + this._symbolDraw.remove(true); + // Remove temporary created elements when highlighting + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + + this._polyline = + this._polygon = + this._coordSys = + this._points = + this._stackedOnPoints = + this._data = null; + } + }); + + +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/SymbolDraw + */ + + + var graphic = __webpack_require__(43); + var Symbol = __webpack_require__(105); + + /** + * @constructor + * @alias module:echarts/chart/helper/SymbolDraw + * @param {module:zrender/graphic/Group} [symbolCtor] + */ + function SymbolDraw(symbolCtor) { + this.group = new graphic.Group(); + + this._symbolCtor = symbolCtor || Symbol; + } + + var symbolDrawProto = SymbolDraw.prototype; + + function symbolNeedsDraw(data, idx, isIgnore) { + var point = data.getItemLayout(idx); + // Is an object + // if (point && point.hasOwnProperty('point')) { + // point = point.point; + // } + return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx)) + && data.getItemVisual(idx, 'symbol') !== 'none'; + } + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + * @param {Array.} [isIgnore] + */ + symbolDrawProto.updateData = function (data, isIgnore) { + var group = this.group; + var seriesModel = data.hostModel; + var oldData = this._data; + + var SymbolCtor = this._symbolCtor; + + var seriesScope = { + itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']), + hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(), + symbolRotate: seriesModel.get('symbolRotate'), + symbolOffset: seriesModel.get('symbolOffset'), + hoverAnimation: seriesModel.get('hoverAnimation'), + + labelModel: seriesModel.getModel('label.normal'), + hoverLabelModel: seriesModel.getModel('label.emphasis') + }; + + data.diff(oldData) + .add(function (newIdx) { + var point = data.getItemLayout(newIdx); + if (symbolNeedsDraw(data, newIdx, isIgnore)) { + var symbolEl = new SymbolCtor(data, newIdx, seriesScope); + symbolEl.attr('position', point); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }) + .update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + var point = data.getItemLayout(newIdx); + if (!symbolNeedsDraw(data, newIdx, isIgnore)) { + group.remove(symbolEl); + return; + } + if (!symbolEl) { + symbolEl = new SymbolCtor(data, newIdx); + symbolEl.attr('position', point); + } + else { + symbolEl.updateData(data, newIdx, seriesScope); + graphic.updateProps(symbolEl, { + position: point + }, seriesModel); + } + + // Add back + group.add(symbolEl); + + data.setItemGraphicEl(newIdx, symbolEl); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && el.fadeOut(function () { + group.remove(el); + }); + }) + .execute(); + + this._data = data; + }; + + symbolDrawProto.updateLayout = function () { + var data = this._data; + if (data) { + // Not use animation + data.eachItemGraphicEl(function (el, idx) { + var point = data.getItemLayout(idx); + el.attr('position', point); + }); + } + }; + + symbolDrawProto.remove = function (enableAnimation) { + var group = this.group; + var data = this._data; + if (data) { + if (enableAnimation) { + data.eachItemGraphicEl(function (el) { + el.fadeOut(function () { + group.remove(el); + }); + }); + } + else { + group.removeAll(); + } + } + }; + + module.exports = SymbolDraw; + + +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Symbol + */ + + + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + + function normalizeSymbolSize(symbolSize) { + if (!(symbolSize instanceof Array)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + + /** + * @constructor + * @alias {module:echarts/chart/helper/Symbol} + * @param {module:echarts/data/List} data + * @param {number} idx + * @extends {module:zrender/graphic/Group} + */ + function Symbol(data, idx, seriesScope) { + graphic.Group.call(this); + + this.updateData(data, idx, seriesScope); + } + + var symbolProto = Symbol.prototype; + + function driftSymbol(dx, dy) { + this.parent.drift(dx, dy); + } + + symbolProto._createSymbol = function (symbolType, data, idx) { + // Remove paths created before + this.removeAll(); + + var seriesModel = data.hostModel; + var color = data.getItemVisual(idx, 'color'); + + var symbolPath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + + symbolPath.attr({ + z2: 100, + culling: true, + scale: [0, 0] + }); + // Rewrite drift method + symbolPath.drift = driftSymbol; + + var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + + graphic.initProps(symbolPath, { + scale: size + }, seriesModel, idx); + + this._symbolType = symbolType; + + this.add(symbolPath); + }; + + /** + * Stop animation + * @param {boolean} toLastFrame + */ + symbolProto.stopSymbolAnimation = function (toLastFrame) { + this.childAt(0).stopAnimation(toLastFrame); + }; + + /** + * Get symbol path element + */ + symbolProto.getSymbolPath = function () { + return this.childAt(0); + }; + + /** + * Get scale(aka, current symbol size). + * Including the change caused by animation + */ + symbolProto.getScale = function () { + return this.childAt(0).scale; + }; + + /** + * Highlight symbol + */ + symbolProto.highlight = function () { + this.childAt(0).trigger('emphasis'); + }; + + /** + * Downplay symbol + */ + symbolProto.downplay = function () { + this.childAt(0).trigger('normal'); + }; + + /** + * @param {number} zlevel + * @param {number} z + */ + symbolProto.setZ = function (zlevel, z) { + var symbolPath = this.childAt(0); + symbolPath.zlevel = zlevel; + symbolPath.z = z; + }; + + symbolProto.setDraggable = function (draggable) { + var symbolPath = this.childAt(0); + symbolPath.draggable = draggable; + symbolPath.cursor = draggable ? 'move' : 'pointer'; + }; + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + symbolProto.updateData = function (data, idx, seriesScope) { + this.silent = false; + + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var seriesModel = data.hostModel; + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + if (symbolType !== this._symbolType) { + this._createSymbol(symbolType, data, idx); + } + else { + var symbolPath = this.childAt(0); + graphic.updateProps(symbolPath, { + scale: symbolSize + }, seriesModel, idx); + } + this._updateCommon(data, idx, symbolSize, seriesScope); + + this._seriesModel = seriesModel; + }; + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + var normalLabelAccessPath = ['label', 'normal']; + var emphasisLabelAccessPath = ['label', 'emphasis']; + + symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) { + var symbolPath = this.childAt(0); + var seriesModel = data.hostModel; + var color = data.getItemVisual(idx, 'color'); + + // Reset style + if (symbolPath.type !== 'image') { + symbolPath.useStyle({ + strokeNoScale: true + }); + } + + seriesScope = seriesScope || null; + + var itemStyle = seriesScope && seriesScope.itemStyle; + var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle; + var symbolRotate = seriesScope && seriesScope.symbolRotate; + var symbolOffset = seriesScope && seriesScope.symbolOffset; + var labelModel = seriesScope && seriesScope.labelModel; + var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; + var hoverAnimation = seriesScope && seriesScope.hoverAnimation; + + if (!seriesScope || data.hasItemOption) { + var itemModel = data.getItemModel(idx); + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']); + hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + + symbolRotate = itemModel.getShallow('symbolRotate'); + symbolOffset = itemModel.getShallow('symbolOffset'); + + labelModel = itemModel.getModel(normalLabelAccessPath); + hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath); + hoverAnimation = itemModel.getShallow('hoverAnimation'); + } + else { + hoverItemStyle = zrUtil.extend({}, hoverItemStyle); + } + + var elStyle = symbolPath.style; + + symbolPath.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + + if (symbolOffset) { + symbolPath.attr('position', [ + numberUtil.parsePercent(symbolOffset[0], symbolSize[0]), + numberUtil.parsePercent(symbolOffset[1], symbolSize[1]) + ]); + } + + // PENDING setColor before setStyle!!! + symbolPath.setColor(color); + + symbolPath.setStyle(itemStyle); + + var opacity = data.getItemVisual(idx, 'opacity'); + if (opacity != null) { + elStyle.opacity = opacity; + } + + // Get last value dim + var dimensions = data.dimensions.slice(); + var valueDim; + var dataType; + while (dimensions.length && ( + valueDim = dimensions.pop(), + dataType = data.getDimensionInfo(valueDim).type, + dataType === 'ordinal' || dataType === 'time' + )) {} // jshint ignore:line + + if (valueDim != null && labelModel.getShallow('show')) { + graphic.setText(elStyle, labelModel, color); + elStyle.text = zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal'), + data.get(valueDim, idx) + ); + } + else { + elStyle.text = ''; + } + + if (valueDim != null && hoverLabelModel.getShallow('show')) { + graphic.setText(hoverItemStyle, hoverLabelModel, color); + hoverItemStyle.text = zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis'), + data.get(valueDim, idx) + ); + } + else { + hoverItemStyle.text = ''; + } + + var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + + symbolPath.off('mouseover') + .off('mouseout') + .off('emphasis') + .off('normal'); + + symbolPath.hoverStyle = hoverItemStyle; + + graphic.setHoverStyle(symbolPath); + + if (hoverAnimation && seriesModel.ifEnableAnimation()) { + var onEmphasis = function() { + var ratio = size[1] / size[0]; + this.animateTo({ + scale: [ + Math.max(size[0] * 1.1, size[0] + 3), + Math.max(size[1] * 1.1, size[1] + 3 * ratio) + ] + }, 400, 'elasticOut'); + }; + var onNormal = function() { + this.animateTo({ + scale: size + }, 400, 'elasticOut'); + }; + symbolPath.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } + }; + + symbolProto.fadeOut = function (cb) { + var symbolPath = this.childAt(0); + // Avoid mistaken hover when fading out + this.silent = true; + // Not show text when animating + symbolPath.style.text = ''; + graphic.updateProps(symbolPath, { + scale: [0, 0] + }, this._seriesModel, this.dataIndex, cb); + }; + + zrUtil.inherits(Symbol, graphic.Group); + + module.exports = Symbol; + + +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Symbol factory + + + var graphic = __webpack_require__(43); + var BoundingRect = __webpack_require__(9); + + /** + * Triangle shape + * @inner + */ + var Triangle = graphic.extendShape({ + type: 'triangle', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy + height); + path.lineTo(cx - width, cy + height); + path.closePath(); + } + }); + /** + * Diamond shape + * @inner + */ + var Diamond = graphic.extendShape({ + type: 'diamond', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy); + path.lineTo(cx, cy + height); + path.lineTo(cx - width, cy); + path.closePath(); + } + }); + + /** + * Pin shape + * @inner + */ + var Pin = graphic.extendShape({ + type: 'pin', + shape: { + // x, y on the cusp + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (path, shape) { + var x = shape.x; + var y = shape.y; + var w = shape.width / 5 * 3; + // Height must be larger than width + var h = Math.max(w, shape.height); + var r = w / 2; + + // Dist on y with tangent point and circle center + var dy = r * r / (h - r); + var cy = y - h + r + dy; + var angle = Math.asin(dy / r); + // Dist on x with tangent point and circle center + var dx = Math.cos(angle) * r; + + var tanX = Math.sin(angle); + var tanY = Math.cos(angle); + + path.arc( + x, cy, r, + Math.PI - angle, + Math.PI * 2 + angle + ); + + var cpLen = r * 0.6; + var cpLen2 = r * 0.7; + path.bezierCurveTo( + x + dx - tanX * cpLen, cy + dy + tanY * cpLen, + x, y - cpLen2, + x, y + ); + path.bezierCurveTo( + x, y - cpLen2, + x - dx + tanX * cpLen, cy + dy + tanY * cpLen, + x - dx, cy + dy + ); + path.closePath(); + } + }); + + /** + * Arrow shape + * @inner + */ + var Arrow = graphic.extendShape({ + + type: 'arrow', + + shape: { + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (ctx, shape) { + var height = shape.height; + var width = shape.width; + var x = shape.x; + var y = shape.y; + var dx = width / 3 * 2; + ctx.moveTo(x, y); + ctx.lineTo(x + dx, y + height); + ctx.lineTo(x, y + height / 4 * 3); + ctx.lineTo(x - dx, y + height); + ctx.lineTo(x, y); + ctx.closePath(); + } + }); + + /** + * Map of path contructors + * @type {Object.} + */ + var symbolCtors = { + line: graphic.Line, + + rect: graphic.Rect, + + roundRect: graphic.Rect, + + square: graphic.Rect, + + circle: graphic.Circle, + + diamond: Diamond, + + pin: Pin, + + arrow: Arrow, + + triangle: Triangle + }; + + var symbolShapeMakers = { + + line: function (x, y, w, h, shape) { + // FIXME + shape.x1 = x; + shape.y1 = y + h / 2; + shape.x2 = x + w; + shape.y2 = y + h / 2; + }, + + rect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + }, + + roundRect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + shape.r = Math.min(w, h) / 4; + }, + + square: function (x, y, w, h, shape) { + var size = Math.min(w, h); + shape.x = x; + shape.y = y; + shape.width = size; + shape.height = size; + }, + + circle: function (x, y, w, h, shape) { + // Put circle in the center of square + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.r = Math.min(w, h) / 2; + }, + + diamond: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + }, + + pin: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + + arrow: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + + triangle: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + } + }; + + var symbolBuildProxies = {}; + for (var name in symbolCtors) { + symbolBuildProxies[name] = new symbolCtors[name](); + } + + var Symbol = graphic.extendShape({ + + type: 'symbol', + + shape: { + symbolType: '', + x: 0, + y: 0, + width: 0, + height: 0 + }, + + beforeBrush: function () { + var style = this.style; + var shape = this.shape; + // FIXME + if (shape.symbolType === 'pin' && style.textPosition === 'inside') { + style.textPosition = ['50%', '40%']; + style.textAlign = 'center'; + style.textVerticalAlign = 'middle'; + } + }, + + buildPath: function (ctx, shape, inBundle) { + var symbolType = shape.symbolType; + var proxySymbol = symbolBuildProxies[symbolType]; + if (shape.symbolType !== 'none') { + if (!proxySymbol) { + // Default rect + symbolType = 'rect'; + proxySymbol = symbolBuildProxies[symbolType]; + } + symbolShapeMakers[symbolType]( + shape.x, shape.y, shape.width, shape.height, proxySymbol.shape + ); + proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle); + } + } + }); + + // Provide setColor helper method to avoid determine if set the fill or stroke outside + var symbolPathSetColor = function (color) { + if (this.type !== 'image') { + var symbolStyle = this.style; + var symbolShape = this.shape; + if (symbolShape && symbolShape.symbolType === 'line') { + symbolStyle.stroke = color; + } + else if (this.__isEmptyBrush) { + symbolStyle.stroke = color; + symbolStyle.fill = '#fff'; + } + else { + // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ? + symbolStyle.fill && (symbolStyle.fill = color); + symbolStyle.stroke && (symbolStyle.stroke = color); + } + this.dirty(false); + } + }; + + var symbolUtil = { + /** + * Create a symbol element with given symbol configuration: shape, x, y, width, height, color + * @param {string} symbolType + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {string} color + */ + createSymbol: function (symbolType, x, y, w, h, color) { + var isEmpty = symbolType.indexOf('empty') === 0; + if (isEmpty) { + symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6); + } + var symbolPath; + + if (symbolType.indexOf('image://') === 0) { + symbolPath = new graphic.Image({ + style: { + image: symbolType.slice(8), + x: x, + y: y, + width: w, + height: h + } + }); + } + else if (symbolType.indexOf('path://') === 0) { + symbolPath = graphic.makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h)); + } + else { + symbolPath = new Symbol({ + shape: { + symbolType: symbolType, + x: x, + y: y, + width: w, + height: h + } + }); + } + + symbolPath.__isEmptyBrush = isEmpty; + + symbolPath.setColor = symbolPathSetColor; + + symbolPath.setColor(color); + + return symbolPath; + } + }; + + module.exports = symbolUtil; + + +/***/ }, +/* 107 */ +/***/ function(module, exports) { + + + + // var arrayDiff = require('zrender/lib/core/arrayDiff'); + // 'zrender/core/arrayDiff' has been used before, but it did + // not do well in performance when roam with fixed dataZoom window. + + function sign(val) { + return val >= 0 ? 1 : -1; + } + + function getStackedOnPoint(coordSys, data, idx) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = baseAxis.onZero + ? 0 : valueAxis.scale.getExtent()[0]; + + var valueDim = valueAxis.dim; + var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0; + + var stackedOnSameSign; + var stackedOn = data.stackedOn; + var val = data.get(valueDim, idx); + // Find first stacked value with same sign + while (stackedOn && + sign(stackedOn.get(valueDim, idx)) === sign(val) + ) { + stackedOnSameSign = stackedOn; + break; + } + var stackedData = []; + stackedData[baseDataOffset] = data.get(baseAxis.dim, idx); + stackedData[1 - baseDataOffset] = stackedOnSameSign + ? stackedOnSameSign.get(valueDim, idx, true) : valueStart; + + return coordSys.dataToPoint(stackedData); + } + + // function convertToIntId(newIdList, oldIdList) { + // // Generate int id instead of string id. + // // Compare string maybe slow in score function of arrDiff + + // // Assume id in idList are all unique + // var idIndicesMap = {}; + // var idx = 0; + // for (var i = 0; i < newIdList.length; i++) { + // idIndicesMap[newIdList[i]] = idx; + // newIdList[i] = idx++; + // } + // for (var i = 0; i < oldIdList.length; i++) { + // var oldId = oldIdList[i]; + // // Same with newIdList + // if (idIndicesMap[oldId]) { + // oldIdList[i] = idIndicesMap[oldId]; + // } + // else { + // oldIdList[i] = idx++; + // } + // } + // } + + function diffData(oldData, newData) { + var diffResult = []; + + newData.diff(oldData) + .add(function (idx) { + diffResult.push({cmd: '+', idx: idx}); + }) + .update(function (newIdx, oldIdx) { + diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx}); + }) + .remove(function (idx) { + diffResult.push({cmd: '-', idx: idx}); + }) + .execute(); + + return diffResult; + } + + module.exports = function ( + oldData, newData, + oldStackedOnPoints, newStackedOnPoints, + oldCoordSys, newCoordSys + ) { + var diff = diffData(oldData, newData); + + // var newIdList = newData.mapArray(newData.getId); + // var oldIdList = oldData.mapArray(oldData.getId); + + // convertToIntId(newIdList, oldIdList); + + // // FIXME One data ? + // diff = arrayDiff(oldIdList, newIdList); + + var currPoints = []; + var nextPoints = []; + // Points for stacking base line + var currStackedPoints = []; + var nextStackedPoints = []; + + var status = []; + var sortedIndices = []; + var rawIndices = []; + var dims = newCoordSys.dimensions; + for (var i = 0; i < diff.length; i++) { + var diffItem = diff[i]; + var pointAdded = true; + + // FIXME, animation is not so perfect when dataZoom window moves fast + // Which is in case remvoing or add more than one data in the tail or head + switch (diffItem.cmd) { + case '=': + var currentPt = oldData.getItemLayout(diffItem.idx); + var nextPt = newData.getItemLayout(diffItem.idx1); + // If previous data is NaN, use next point directly + if (isNaN(currentPt[0]) || isNaN(currentPt[1])) { + currentPt = nextPt.slice(); + } + currPoints.push(currentPt); + nextPoints.push(nextPt); + + currStackedPoints.push(oldStackedOnPoints[diffItem.idx]); + nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]); + + rawIndices.push(newData.getRawIndex(diffItem.idx1)); + break; + case '+': + var idx = diffItem.idx; + currPoints.push( + oldCoordSys.dataToPoint([ + newData.get(dims[0], idx, true), newData.get(dims[1], idx, true) + ]) + ); + + nextPoints.push(newData.getItemLayout(idx).slice()); + + currStackedPoints.push( + getStackedOnPoint(oldCoordSys, newData, idx) + ); + nextStackedPoints.push(newStackedOnPoints[idx]); + + rawIndices.push(newData.getRawIndex(idx)); + break; + case '-': + var idx = diffItem.idx; + var rawIndex = oldData.getRawIndex(idx); + // Data is replaced. In the case of dynamic data queue + // FIXME FIXME FIXME + if (rawIndex !== idx) { + currPoints.push(oldData.getItemLayout(idx)); + nextPoints.push(newCoordSys.dataToPoint([ + oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true) + ])); + + currStackedPoints.push(oldStackedOnPoints[idx]); + nextStackedPoints.push( + getStackedOnPoint( + newCoordSys, oldData, idx + ) + ); + + rawIndices.push(rawIndex); + } + else { + pointAdded = false; + } + } + + // Original indices + if (pointAdded) { + status.push(diffItem); + sortedIndices.push(sortedIndices.length); + } + } + + // Diff result may be crossed if all items are changed + // Sort by data index + sortedIndices.sort(function (a, b) { + return rawIndices[a] - rawIndices[b]; + }); + + var sortedCurrPoints = []; + var sortedNextPoints = []; + + var sortedCurrStackedPoints = []; + var sortedNextStackedPoints = []; + + var sortedStatus = []; + for (var i = 0; i < sortedIndices.length; i++) { + var idx = sortedIndices[i]; + sortedCurrPoints[i] = currPoints[idx]; + sortedNextPoints[i] = nextPoints[idx]; + + sortedCurrStackedPoints[i] = currStackedPoints[idx]; + sortedNextStackedPoints[i] = nextStackedPoints[idx]; + + sortedStatus[i] = status[idx]; + } + + return { + current: sortedCurrPoints, + next: sortedNextPoints, + + stackedOnCurrent: sortedCurrStackedPoints, + stackedOnNext: sortedNextStackedPoints, + + status: sortedStatus + }; + }; + + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + // Poly path support NaN point + + + var Path = __webpack_require__(45); + var vec2 = __webpack_require__(10); + + var vec2Min = vec2.min; + var vec2Max = vec2.max; + + var scaleAndAdd = vec2.scaleAndAdd; + var v2Copy = vec2.copy; + + // Temporary variable + var v = []; + var cp0 = []; + var cp1 = []; + + function isPointNull(p) { + return isNaN(p[0]) || isNaN(p[1]); + } + + function drawSegment( + ctx, points, start, segLen, allLen, + dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls + ) { + var prevIdx = 0; + var idx = start; + for (var k = 0; k < segLen; k++) { + var p = points[idx]; + if (idx >= allLen || idx < 0) { + break; + } + if (isPointNull(p)) { + if (connectNulls) { + idx += dir; + continue; + } + break; + } + + if (idx === start) { + ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); + v2Copy(cp0, p); + } + else { + if (smooth > 0) { + var nextIdx = idx + dir; + var nextP = points[nextIdx]; + if (connectNulls) { + // Find next point not null + while (nextP && isPointNull(points[nextIdx])) { + nextIdx += dir; + nextP = points[nextIdx]; + } + } + + var ratioNextSeg = 0.5; + var prevP = points[prevIdx]; + var nextP = points[nextIdx]; + // Last point + if (!nextP || isPointNull(nextP)) { + v2Copy(cp1, p); + } + else { + // If next data is null in not connect case + if (isPointNull(nextP) && !connectNulls) { + nextP = p; + } + + vec2.sub(v, nextP, prevP); + + var lenPrevSeg; + var lenNextSeg; + if (smoothMonotone === 'x' || smoothMonotone === 'y') { + var dim = smoothMonotone === 'x' ? 0 : 1; + lenPrevSeg = Math.abs(p[dim] - prevP[dim]); + lenNextSeg = Math.abs(p[dim] - nextP[dim]); + } + else { + lenPrevSeg = vec2.dist(p, prevP); + lenNextSeg = vec2.dist(p, nextP); + } + + // Use ratio of seg length + ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); + + scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg)); + } + // Smooth constraint + vec2Min(cp0, cp0, smoothMax); + vec2Max(cp0, cp0, smoothMin); + vec2Min(cp1, cp1, smoothMax); + vec2Max(cp1, cp1, smoothMin); + + ctx.bezierCurveTo( + cp0[0], cp0[1], + cp1[0], cp1[1], + p[0], p[1] + ); + // cp0 of next segment + scaleAndAdd(cp0, p, v, smooth * ratioNextSeg); + } + else { + ctx.lineTo(p[0], p[1]); + } + } + + prevIdx = idx; + idx += dir; + } + + return k; + } + + function getBoundingBox(points, smoothConstraint) { + var ptMin = [Infinity, Infinity]; + var ptMax = [-Infinity, -Infinity]; + if (smoothConstraint) { + for (var i = 0; i < points.length; i++) { + var pt = points[i]; + if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; } + if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; } + if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; } + if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; } + } + } + return { + min: smoothConstraint ? ptMin : ptMax, + max: smoothConstraint ? ptMax : ptMin + }; + } + + module.exports = { + + Polyline: Path.extend({ + + type: 'ec-polyline', + + shape: { + points: [], + + smooth: 0, + + smoothConstraint: true, + + smoothMonotone: null, + + connectNulls: false + }, + + style: { + fill: null, + + stroke: '#000' + }, + + buildPath: function (ctx, shape) { + var points = shape.points; + + var i = 0; + var len = points.length; + + var result = getBoundingBox(points, shape.smoothConstraint); + + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len) { + i += drawSegment( + ctx, points, i, len, len, + 1, result.min, result.max, shape.smooth, + shape.smoothMonotone, shape.connectNulls + ) + 1; + } + } + }), + + Polygon: Path.extend({ + + type: 'ec-polygon', + + shape: { + points: [], + + // Offset between stacked base points and points + stackedOnPoints: [], + + smooth: 0, + + stackedOnSmooth: 0, + + smoothConstraint: true, + + smoothMonotone: null, + + connectNulls: false + }, + + buildPath: function (ctx, shape) { + var points = shape.points; + var stackedOnPoints = shape.stackedOnPoints; + + var i = 0; + var len = points.length; + var smoothMonotone = shape.smoothMonotone; + var bbox = getBoundingBox(points, shape.smoothConstraint); + var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint); + + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len) { + var k = drawSegment( + ctx, points, i, len, len, + 1, bbox.min, bbox.max, shape.smooth, + smoothMonotone, shape.connectNulls + ); + drawSegment( + ctx, stackedOnPoints, i + k - 1, k, len, + -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, + smoothMonotone, shape.connectNulls + ); + i += k + 1; + + ctx.closePath(); + } + } + }) + }; + + +/***/ }, +/* 109 */ +/***/ function(module, exports) { + + + + module.exports = function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) { + + // Encoding visual for all series include which is filtered for legend drawing + ecModel.eachRawSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + + var symbolType = seriesModel.get('symbol') || defaultSymbolType; + var symbolSize = seriesModel.get('symbolSize'); + + data.setVisual({ + legendSymbol: legendSymbol || symbolType, + symbol: symbolType, + symbolSize: symbolSize + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + if (typeof symbolSize === 'function') { + data.each(function (idx) { + var rawValue = seriesModel.getRawValue(idx); + // FIXME + var params = seriesModel.getDataParams(idx); + data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params)); + }); + } + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var itemSymbolType = itemModel.getShallow('symbol', true); + var itemSymbolSize = itemModel.getShallow('symbolSize', true); + // If has item symbol + if (itemSymbolType != null) { + data.setItemVisual(idx, 'symbol', itemSymbolType); + } + if (itemSymbolSize != null) { + // PENDING Transform symbolSize ? + data.setItemVisual(idx, 'symbolSize', itemSymbolSize); + } + }); + } + }); + }; + + +/***/ }, +/* 110 */ +/***/ function(module, exports) { + + + + module.exports = function (seriesType, ecModel) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + + if (coordSys) { + var dims = coordSys.dimensions; + + if (coordSys.type === 'singleAxis') { + data.each(dims[0], function (x, idx) { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x)); + }); + } + else { + data.each(dims, function (x, y, idx) { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout( + idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y]) + ); + }, true); + } + } + }); + }; + + +/***/ }, +/* 111 */ +/***/ function(module, exports) { + + + var samplers = { + average: function (frame) { + var sum = 0; + var count = 0; + for (var i = 0; i < frame.length; i++) { + if (!isNaN(frame[i])) { + sum += frame[i]; + count++; + } + } + // Return NaN if count is 0 + return count === 0 ? NaN : sum / count; + }, + sum: function (frame) { + var sum = 0; + for (var i = 0; i < frame.length; i++) { + // Ignore NaN + sum += frame[i] || 0; + } + return sum; + }, + max: function (frame) { + var max = -Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] > max && (max = frame[i]); + } + return max; + }, + min: function (frame) { + var min = Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] < min && (min = frame[i]); + } + return min; + }, + // TODO + // Median + nearest: function (frame) { + return frame[0]; + } + }; + + var indexSampler = function (frame, value) { + return Math.round(frame.length / 2); + }; + module.exports = function (seriesType, ecModel, api) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var sampling = seriesModel.get('sampling'); + var coordSys = seriesModel.coordinateSystem; + // Only cartesian2d support down sampling + if (coordSys.type === 'cartesian2d' && sampling) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var extent = baseAxis.getExtent(); + // Coordinste system has been resized + var size = extent[1] - extent[0]; + var rate = Math.round(data.count() / size); + if (rate > 1) { + var sampler; + if (typeof sampling === 'string') { + sampler = samplers[sampling]; + } + else if (typeof sampling === 'function') { + sampler = sampling; + } + if (sampler) { + data = data.downSample( + valueAxis.dim, 1 / rate, sampler, indexSampler + ); + seriesModel.setData(data); + } + } + } + }, this); + }; + + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(113); + + __webpack_require__(130); + + // Grid view + echarts.extendComponentView({ + + type: 'grid', + + render: function (gridModel, ecModel) { + this.group.removeAll(); + if (gridModel.get('show')) { + this.group.add(new graphic.Rect({ + shape:gridModel.coordinateSystem.getRect(), + style: zrUtil.defaults({ + fill: gridModel.get('backgroundColor') + }, gridModel.getItemStyle()), + silent: true + })); + } + } + }); + + echarts.registerPreprocessor(function (option) { + // Only create grid when need + if (option.xAxis && option.yAxis && !option.grid) { + option.grid = {}; + } + }); + + +/***/ }, +/* 113 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Grid is a region which contains at most 4 cartesian systems + * + * TODO Default cartesian + */ + var factory = exports; + + var layout = __webpack_require__(21); + var axisHelper = __webpack_require__(114); + + var zrUtil = __webpack_require__(4); + var Cartesian2D = __webpack_require__(120); + var Axis2D = __webpack_require__(122); + + var each = zrUtil.each; + + var ifAxisCrossZero = axisHelper.ifAxisCrossZero; + var niceScaleExtent = axisHelper.niceScaleExtent; + + // 依赖 GridModel, AxisModel 做预处理 + __webpack_require__(125); + + /** + * Check if the axis is used in the specified grid + * @inner + */ + function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) { + return axisModel.findGridModel() === gridModel; + } + + function getLabelUnionRect(axis) { + var axisModel = axis.model; + var labels = axisModel.getFormattedLabels(); + var rect; + var step = 1; + var labelCount = labels.length; + if (labelCount > 40) { + // Simple optimization for large amount of labels + step = Math.ceil(labelCount / 40); + } + for (var i = 0; i < labelCount; i += step) { + if (!axis.isLabelIgnored(i)) { + var singleRect = axisModel.getTextRect(labels[i]); + // FIXME consider label rotate + rect ? rect.union(singleRect) : (rect = singleRect); + } + } + return rect; + } + + function Grid(gridModel, ecModel, api) { + /** + * @type {Object.} + * @private + */ + this._coordsMap = {}; + + /** + * @type {Array.} + * @private + */ + this._coordsList = []; + + /** + * @type {Object.} + * @private + */ + this._axesMap = {}; + + /** + * @type {Array.} + * @private + */ + this._axesList = []; + + this._initCartesian(gridModel, ecModel, api); + + this._model = gridModel; + } + + var gridProto = Grid.prototype; + + gridProto.type = 'grid'; + + gridProto.getRect = function () { + return this._rect; + }; + + gridProto.update = function (ecModel, api) { + + var axesMap = this._axesMap; + + this._updateScale(ecModel, this._model); + + function ifAxisCanNotOnZero(otherAxisDim) { + var axes = axesMap[otherAxisDim]; + for (var idx in axes) { + var axis = axes[idx]; + if (axis && (axis.type === 'category' || !ifAxisCrossZero(axis))) { + return true; + } + } + return false; + } + + each(axesMap.x, function (xAxis) { + niceScaleExtent(xAxis, xAxis.model); + }); + each(axesMap.y, function (yAxis) { + niceScaleExtent(yAxis, yAxis.model); + }); + // Fix configuration + each(axesMap.x, function (xAxis) { + // onZero can not be enabled in these two situations + // 1. When any other axis is a category axis + // 2. When any other axis not across 0 point + if (ifAxisCanNotOnZero('y')) { + xAxis.onZero = false; + } + }); + each(axesMap.y, function (yAxis) { + if (ifAxisCanNotOnZero('x')) { + yAxis.onZero = false; + } + }); + + // Resize again if containLabel is enabled + // FIXME It may cause getting wrong grid size in data processing stage + this.resize(this._model, api); + }; + + /** + * Resize the grid + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {module:echarts/ExtensionAPI} api + */ + gridProto.resize = function (gridModel, api) { + + var gridRect = layout.getLayoutRect( + gridModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + + this._rect = gridRect; + + var axesList = this._axesList; + + adjustAxes(); + + // Minus label size + if (gridModel.get('containLabel')) { + each(axesList, function (axis) { + if (!axis.model.get('axisLabel.inside')) { + var labelUnionRect = getLabelUnionRect(axis); + if (labelUnionRect) { + var dim = axis.isHorizontal() ? 'height' : 'width'; + var margin = axis.model.get('axisLabel.margin'); + gridRect[dim] -= labelUnionRect[dim] + margin; + if (axis.position === 'top') { + gridRect.y += labelUnionRect.height + margin; + } + else if (axis.position === 'left') { + gridRect.x += labelUnionRect.width + margin; + } + } + } + }); + + adjustAxes(); + } + + function adjustAxes() { + each(axesList, function (axis) { + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(extent[idx], extent[1 - idx]); + updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y); + }); + } + }; + + /** + * @param {string} axisType + * @param {ndumber} [axisIndex] + */ + gridProto.getAxis = function (axisType, axisIndex) { + var axesMapOnDim = this._axesMap[axisType]; + if (axesMapOnDim != null) { + if (axisIndex == null) { + // Find first axis + for (var name in axesMapOnDim) { + return axesMapOnDim[name]; + } + } + return axesMapOnDim[axisIndex]; + } + }; + + gridProto.getCartesian = function (xAxisIndex, yAxisIndex) { + if (xAxisIndex != null && yAxisIndex != null) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + return this._coordsMap[key]; + } + else { + // When only xAxisIndex or yAxisIndex given, find its first cartesian. + for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) { + if (coordList[i].getAxis('x').index === xAxisIndex + || coordList[i].getAxis('y').index === yAxisIndex + ) { + return coordList[i]; + } + } + } + }; + + /** + * Initialize cartesian coordinate systems + * @private + */ + gridProto._initCartesian = function (gridModel, ecModel, api) { + var axisPositionUsed = { + left: false, + right: false, + top: false, + bottom: false + }; + + var axesMap = { + x: {}, + y: {} + }; + var axesCount = { + x: 0, + y: 0 + }; + + /// Create axis + ecModel.eachComponent('xAxis', createAxisCreator('x'), this); + ecModel.eachComponent('yAxis', createAxisCreator('y'), this); + + if (!axesCount.x || !axesCount.y) { + // Roll back when there no either x or y axis + this._axesMap = {}; + this._axesList = []; + return; + } + + this._axesMap = axesMap; + + /// Create cartesian2d + each(axesMap.x, function (xAxis, xAxisIndex) { + each(axesMap.y, function (yAxis, yAxisIndex) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + var cartesian = new Cartesian2D(key); + + cartesian.grid = this; + + this._coordsMap[key] = cartesian; + this._coordsList.push(cartesian); + + cartesian.addAxis(xAxis); + cartesian.addAxis(yAxis); + }, this); + }, this); + + function createAxisCreator(axisType) { + return function (axisModel, idx) { + if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) { + return; + } + + var axisPosition = axisModel.get('position'); + if (axisType === 'x') { + // Fix position + if (axisPosition !== 'top' && axisPosition !== 'bottom') { + // Default bottom of X + axisPosition = 'bottom'; + if (axisPositionUsed[axisPosition]) { + axisPosition = axisPosition === 'top' ? 'bottom' : 'top'; + } + } + } + else { + // Fix position + if (axisPosition !== 'left' && axisPosition !== 'right') { + // Default left of Y + axisPosition = 'left'; + if (axisPositionUsed[axisPosition]) { + axisPosition = axisPosition === 'left' ? 'right' : 'left'; + } + } + } + axisPositionUsed[axisPosition] = true; + + var axis = new Axis2D( + axisType, axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisPosition + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + + axis.onZero = axisModel.get('axisLine.onZero'); + + // Inject axis into axisModel + axisModel.axis = axis; + + // Inject axisModel into axis + axis.model = axisModel; + + // Inject grid info axis + axis.grid = this; + + // Index of axis, can be used as key + axis.index = idx; + + this._axesList.push(axis); + + axesMap[axisType][idx] = axis; + axesCount[axisType]++; + }; + } + }; + + /** + * Update cartesian properties from series + * @param {module:echarts/model/Option} option + * @private + */ + gridProto._updateScale = function (ecModel, gridModel) { + // Reset scale + zrUtil.each(this._axesList, function (axis) { + axis.scale.setExtent(Infinity, -Infinity); + }); + ecModel.eachSeries(function (seriesModel) { + if (isCartesian2D(seriesModel)) { + var axesModels = findAxesModels(seriesModel, ecModel); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel) + || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel) + ) { + return; + } + + var cartesian = this.getCartesian( + xAxisModel.componentIndex, yAxisModel.componentIndex + ); + var data = seriesModel.getData(); + var xAxis = cartesian.getAxis('x'); + var yAxis = cartesian.getAxis('y'); + + if (data.type === 'list') { + unionExtent(data, xAxis, seriesModel); + unionExtent(data, yAxis, seriesModel); + } + } + }, this); + + function unionExtent(data, axis, seriesModel) { + each(seriesModel.coordDimToDataDim(axis.dim), function (dim) { + axis.scale.unionExtent(data.getDataExtent( + dim, axis.scale.type !== 'ordinal' + )); + }); + } + }; + + /** + * @inner + */ + function updateAxisTransfrom(axis, coordBase) { + var axisExtent = axis.getExtent(); + var axisExtentSum = axisExtent[0] + axisExtent[1]; + + // Fast transform + axis.toGlobalCoord = axis.dim === 'x' + ? function (coord) { + return coord + coordBase; + } + : function (coord) { + return axisExtentSum - coord + coordBase; + }; + axis.toLocalCoord = axis.dim === 'x' + ? function (coord) { + return coord - coordBase; + } + : function (coord) { + return axisExtentSum - coord + coordBase; + }; + } + + var axesTypes = ['xAxis', 'yAxis']; + /** + * @inner + */ + function findAxesModels(seriesModel, ecModel) { + return zrUtil.map(axesTypes, function (axisType) { + var axisModel = ecModel.queryComponents({ + mainType: axisType, + index: seriesModel.get(axisType + 'Index'), + id: seriesModel.get(axisType + 'Id') + })[0]; + + if (true) { + if (!axisModel) { + throw new Error(axisType + ' "' + zrUtil.retrieve( + seriesModel.get(axisType + 'Index'), + seriesModel.get(axisType + 'Id'), + 0 + ) + '" not found'); + } + } + return axisModel; + }); + } + + /** + * @inner + */ + function isCartesian2D(seriesModel) { + return seriesModel.get('coordinateSystem') === 'cartesian2d'; + } + + Grid.create = function (ecModel, api) { + var grids = []; + ecModel.eachComponent('grid', function (gridModel, idx) { + var grid = new Grid(gridModel, ecModel, api); + grid.name = 'grid_' + idx; + grid.resize(gridModel, api); + + gridModel.coordinateSystem = grid; + + grids.push(grid); + }); + + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (!isCartesian2D(seriesModel)) { + return; + } + + var axesModels = findAxesModels(seriesModel, ecModel); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + var gridModel = xAxisModel.findGridModel(); + + if (true) { + if (!gridModel) { + throw new Error( + 'Grid "' + zrUtil.retrieve( + xAxisModel.get('gridIndex'), + xAxisModel.get('gridId'), + 0 + ) + '" not found' + ); + } + if (xAxisModel.findGridModel() !== yAxisModel.findGridModel()) { + throw new Error('xAxis and yAxis must use the same grid'); + } + } + + var grid = gridModel.coordinateSystem; + + seriesModel.coordinateSystem = grid.getCartesian( + xAxisModel.componentIndex, yAxisModel.componentIndex + ); + }); + + return grids; + }; + + // For deciding which dimensions to use when creating list data + Grid.dimensions = Cartesian2D.prototype.dimensions; + + __webpack_require__(26).register('cartesian2d', Grid); + + module.exports = Grid; + + +/***/ }, +/* 114 */ +/***/ function(module, exports, __webpack_require__) { + + + + var OrdinalScale = __webpack_require__(115); + var IntervalScale = __webpack_require__(117); + __webpack_require__(118); + __webpack_require__(119); + var Scale = __webpack_require__(116); + + var numberUtil = __webpack_require__(7); + var zrUtil = __webpack_require__(4); + var textContain = __webpack_require__(8); + var axisHelper = {}; + + /** + * Get axis scale extent before niced. + */ + axisHelper.getScaleExtent = function (axis, model) { + var scale = axis.scale; + var originalExtent = scale.getExtent(); + var span = originalExtent[1] - originalExtent[0]; + if (scale.type === 'ordinal') { + // If series has no data, scale extent may be wrong + if (!isFinite(span)) { + return [0, 0]; + } + else { + return originalExtent; + } + } + var min = model.getMin ? model.getMin() : model.get('min'); + var max = model.getMax ? model.getMax() : model.get('max'); + var crossZero = model.getNeedCrossZero + ? model.getNeedCrossZero() : !model.get('scale'); + var boundaryGap = model.get('boundaryGap'); + if (!zrUtil.isArray(boundaryGap)) { + boundaryGap = [boundaryGap || 0, boundaryGap || 0]; + } + boundaryGap[0] = numberUtil.parsePercent(boundaryGap[0], 1); + boundaryGap[1] = numberUtil.parsePercent(boundaryGap[1], 1); + var fixMin = true; + var fixMax = true; + // Add boundary gap + if (min == null) { + min = originalExtent[0] - boundaryGap[0] * span; + fixMin = false; + } + if (max == null) { + max = originalExtent[1] + boundaryGap[1] * span; + fixMax = false; + } + if (min === 'dataMin') { + min = originalExtent[0]; + } + if (max === 'dataMax') { + max = originalExtent[1]; + } + // Evaluate if axis needs cross zero + if (crossZero) { + // Axis is over zero and min is not set + if (min > 0 && max > 0 && !fixMin) { + min = 0; + } + // Axis is under zero and max is not set + if (min < 0 && max < 0 && !fixMax) { + max = 0; + } + } + return [min, max]; + }; + + axisHelper.niceScaleExtent = function (axis, model) { + var scale = axis.scale; + var extent = axisHelper.getScaleExtent(axis, model); + var fixMin = (model.getMin ? model.getMin() : model.get('min')) != null; + var fixMax = (model.getMax ? model.getMax() : model.get('max')) != null; + var splitNumber = model.get('splitNumber'); + + if (scale.type === 'log') { + scale.base = model.get('logBase'); + } + + scale.setExtent(extent[0], extent[1]); + scale.niceExtent(splitNumber, fixMin, fixMax); + + // Use minInterval to constraint the calculated interval. + // If calculated interval is less than minInterval. increase the interval quantity until + // it is larger than minInterval. + // For example: + // minInterval is 1, calculated interval is 0.2, so increase it to be 1. In this way we can get + // an integer axis. + var minInterval = model.get('minInterval'); + if (isFinite(minInterval) && !fixMin && !fixMax && scale.type === 'interval') { + var interval = scale.getInterval(); + var intervalScale = Math.max(Math.abs(interval), minInterval) / interval; + // while (interval < minInterval) { + // var quantity = numberUtil.quantity(interval); + // interval = quantity * 10; + // scaleQuantity *= 10; + // } + extent = scale.getExtent(); + scale.setExtent(intervalScale * extent[0], extent[1] * intervalScale); + scale.niceExtent(splitNumber); + } + + // If some one specified the min, max. And the default calculated interval + // is not good enough. He can specify the interval. It is often appeared + // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard + // to be 60. + // FIXME + var interval = model.get('interval'); + if (interval != null) { + scale.setInterval && scale.setInterval(interval); + } + }; + + /** + * @param {module:echarts/model/Model} model + * @param {string} [axisType] Default retrieve from model.type + * @return {module:echarts/scale/*} + */ + axisHelper.createScaleByModel = function(model, axisType) { + axisType = axisType || model.get('type'); + if (axisType) { + switch (axisType) { + // Buildin scale + case 'category': + return new OrdinalScale( + model.getCategories(), [Infinity, -Infinity] + ); + case 'value': + return new IntervalScale(); + // Extended scale, like time and log + default: + return (Scale.getClass(axisType) || IntervalScale).create(model); + } + } + }; + + /** + * Check if the axis corss 0 + */ + axisHelper.ifAxisCrossZero = function (axis) { + var dataExtent = axis.scale.getExtent(); + var min = dataExtent[0]; + var max = dataExtent[1]; + return !((min > 0 && max > 0) || (min < 0 && max < 0)); + }; + + /** + * @param {Array.} tickCoords In axis self coordinate. + * @param {Array.} labels + * @param {string} font + * @param {boolean} isAxisHorizontal + * @return {number} + */ + axisHelper.getAxisLabelInterval = function (tickCoords, labels, font, isAxisHorizontal) { + // FIXME + // 不同角的axis和label,不只是horizontal和vertical. + + var textSpaceTakenRect; + var autoLabelInterval = 0; + var accumulatedLabelInterval = 0; + + var step = 1; + if (labels.length > 40) { + // Simple optimization for large amount of labels + step = Math.floor(labels.length / 40); + } + + for (var i = 0; i < tickCoords.length; i += step) { + var tickCoord = tickCoords[i]; + var rect = textContain.getBoundingRect( + labels[i], font, 'center', 'top' + ); + rect[isAxisHorizontal ? 'x' : 'y'] += tickCoord; + // FIXME Magic number 1.5 + rect[isAxisHorizontal ? 'width' : 'height'] *= 1.3; + if (!textSpaceTakenRect) { + textSpaceTakenRect = rect.clone(); + } + // There is no space for current label; + else if (textSpaceTakenRect.intersect(rect)) { + accumulatedLabelInterval++; + autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval); + } + else { + textSpaceTakenRect.union(rect); + // Reset + accumulatedLabelInterval = 0; + } + } + if (autoLabelInterval === 0 && step > 1) { + return step; + } + return (autoLabelInterval + 1) * step - 1; + }; + + /** + * @param {Object} axis + * @param {Function} labelFormatter + * @return {Array.} + */ + axisHelper.getFormattedLabels = function (axis, labelFormatter) { + var scale = axis.scale; + var labels = scale.getTicksLabels(); + var ticks = scale.getTicks(); + if (typeof labelFormatter === 'string') { + labelFormatter = (function (tpl) { + return function (val) { + return tpl.replace('{value}', val); + }; + })(labelFormatter); + return zrUtil.map(labels, labelFormatter); + } + else if (typeof labelFormatter === 'function') { + return zrUtil.map(ticks, function (tick, idx) { + return labelFormatter( + axis.type === 'category' ? scale.getLabel(tick) : tick, + idx + ); + }, this); + } + else { + return labels; + } + }; + + module.exports = axisHelper; + + +/***/ }, +/* 115 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Linear continuous scale + * @module echarts/coord/scale/Ordinal + * + * http://en.wikipedia.org/wiki/Level_of_measurement + */ + + // FIXME only one data + + + var zrUtil = __webpack_require__(4); + var Scale = __webpack_require__(116); + + var scaleProto = Scale.prototype; + + var OrdinalScale = Scale.extend({ + + type: 'ordinal', + + init: function (data, extent) { + this._data = data; + this._extent = extent || [0, data.length - 1]; + }, + + parse: function (val) { + return typeof val === 'string' + ? zrUtil.indexOf(this._data, val) + // val might be float. + : Math.round(val); + }, + + contain: function (rank) { + rank = this.parse(rank); + return scaleProto.contain.call(this, rank) + && this._data[rank] != null; + }, + + /** + * Normalize given rank or name to linear [0, 1] + * @param {number|string} [val] + * @return {number} + */ + normalize: function (val) { + return scaleProto.normalize.call(this, this.parse(val)); + }, + + scale: function (val) { + return Math.round(scaleProto.scale.call(this, val)); + }, + + /** + * @return {Array} + */ + getTicks: function () { + var ticks = []; + var extent = this._extent; + var rank = extent[0]; + + while (rank <= extent[1]) { + ticks.push(rank); + rank++; + } + + return ticks; + }, + + /** + * Get item on rank n + * @param {number} n + * @return {string} + */ + getLabel: function (n) { + return this._data[n]; + }, + + /** + * @return {number} + */ + count: function () { + return this._extent[1] - this._extent[0] + 1; + }, + + niceTicks: zrUtil.noop, + niceExtent: zrUtil.noop + }); + + /** + * @return {module:echarts/scale/Time} + */ + OrdinalScale.create = function () { + return new OrdinalScale(); + }; + + module.exports = OrdinalScale; + + +/***/ }, +/* 116 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * // Scale class management + * @module echarts/scale/Scale + */ + + + var clazzUtil = __webpack_require__(13); + + function Scale() { + /** + * Extent + * @type {Array.} + * @protected + */ + this._extent = [Infinity, -Infinity]; + + /** + * Step is calculated in adjustExtent + * @type {Array.} + * @protected + */ + this._interval = 0; + + this.init && this.init.apply(this, arguments); + } + + var scaleProto = Scale.prototype; + + /** + * Parse input val to valid inner number. + * @param {*} val + * @return {number} + */ + scaleProto.parse = function (val) { + // Notice: This would be a trap here, If the implementation + // of this method depends on extent, and this method is used + // before extent set (like in dataZoom), it would be wrong. + // Nevertheless, parse does not depend on extent generally. + return val; + }; + + scaleProto.contain = function (val) { + var extent = this._extent; + return val >= extent[0] && val <= extent[1]; + }; + + /** + * Normalize value to linear [0, 1], return 0.5 if extent span is 0 + * @param {number} val + * @return {number} + */ + scaleProto.normalize = function (val) { + var extent = this._extent; + if (extent[1] === extent[0]) { + return 0.5; + } + return (val - extent[0]) / (extent[1] - extent[0]); + }; + + /** + * Scale normalized value + * @param {number} val + * @return {number} + */ + scaleProto.scale = function (val) { + var extent = this._extent; + return val * (extent[1] - extent[0]) + extent[0]; + }; + + /** + * Set extent from data + * @param {Array.} other + */ + scaleProto.unionExtent = function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + // not setExtent because in log axis it may transformed to power + // this.setExtent(extent[0], extent[1]); + }; + + /** + * Get extent + * @return {Array.} + */ + scaleProto.getExtent = function () { + return this._extent.slice(); + }; + + /** + * Set extent + * @param {number} start + * @param {number} end + */ + scaleProto.setExtent = function (start, end) { + var thisExtent = this._extent; + if (!isNaN(start)) { + thisExtent[0] = start; + } + if (!isNaN(end)) { + thisExtent[1] = end; + } + }; + + /** + * @return {Array.} + */ + scaleProto.getTicksLabels = function () { + var labels = []; + var ticks = this.getTicks(); + for (var i = 0; i < ticks.length; i++) { + labels.push(this.getLabel(ticks[i])); + } + return labels; + }; + + clazzUtil.enableClassExtend(Scale); + clazzUtil.enableClassManagement(Scale, { + registerWhenExtend: true + }); + + module.exports = Scale; + + +/***/ }, +/* 117 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Interval scale + * @module echarts/scale/Interval + */ + + + + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var Scale = __webpack_require__(116); + + var mathFloor = Math.floor; + var mathCeil = Math.ceil; + + var getPrecisionSafe = numberUtil.getPrecisionSafe; + var roundingErrorFix = numberUtil.round; + /** + * @alias module:echarts/coord/scale/Interval + * @constructor + */ + var IntervalScale = Scale.extend({ + + type: 'interval', + + _interval: 0, + + setExtent: function (start, end) { + var thisExtent = this._extent; + //start,end may be a Number like '25',so... + if (!isNaN(start)) { + thisExtent[0] = parseFloat(start); + } + if (!isNaN(end)) { + thisExtent[1] = parseFloat(end); + } + }, + + unionExtent: function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + + // unionExtent may called by it's sub classes + IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]); + }, + /** + * Get interval + */ + getInterval: function () { + if (!this._interval) { + this.niceTicks(); + } + return this._interval; + }, + + /** + * Set interval + */ + setInterval: function (interval) { + this._interval = interval; + // Dropped auto calculated niceExtent and use user setted extent + // We assume user wan't to set both interval, min, max to get a better result + this._niceExtent = this._extent.slice(); + }, + + /** + * @return {Array.} + */ + getTicks: function () { + if (!this._interval) { + this.niceTicks(); + } + var interval = this._interval; + var extent = this._extent; + var ticks = []; + + // Consider this case: using dataZoom toolbox, zoom and zoom. + var safeLimit = 10000; + + if (interval) { + var niceExtent = this._niceExtent; + var precision = getPrecisionSafe(interval) + 2; + + if (extent[0] < niceExtent[0]) { + ticks.push(extent[0]); + } + var tick = niceExtent[0]; + while (tick <= niceExtent[1]) { + ticks.push(tick); + // Avoid rounding error + tick = roundingErrorFix(tick + interval, precision); + if (ticks.length > safeLimit) { + return []; + } + } + if (extent[1] > niceExtent[1]) { + ticks.push(extent[1]); + } + } + + return ticks; + }, + + /** + * @return {Array.} + */ + getTicksLabels: function () { + var labels = []; + var ticks = this.getTicks(); + for (var i = 0; i < ticks.length; i++) { + labels.push(this.getLabel(ticks[i])); + } + return labels; + }, + + /** + * @param {number} n + * @return {number} + */ + getLabel: function (data) { + return formatUtil.addCommas(data); + }, + + /** + * Update interval and extent of intervals for nice ticks + * + * @param {number} [splitNumber = 5] Desired number of ticks + */ + niceTicks: function (splitNumber) { + splitNumber = splitNumber || 5; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (!isFinite(span)) { + return; + } + // User may set axis min 0 and data are all negative + // FIXME If it needs to reverse ? + if (span < 0) { + span = -span; + extent.reverse(); + } + + // From "Nice Numbers for Graph Labels" of Graphic Gems + // var niceSpan = numberUtil.nice(span, false); + var step = roundingErrorFix( + numberUtil.nice(span / splitNumber, true), + Math.max( + getPrecisionSafe(extent[0]), + getPrecisionSafe(extent[1]) + // extent may be [0, 1], and step should have 1 more digits. + // To make it safe we add 2 more digits + ) + 2 + ); + + var precision = getPrecisionSafe(step) + 2; + // Niced extent inside original extent + var niceExtent = [ + roundingErrorFix(mathCeil(extent[0] / step) * step, precision), + roundingErrorFix(mathFloor(extent[1] / step) * step, precision) + ]; + + this._interval = step; + this._niceExtent = niceExtent; + }, + + /** + * Nice extent. + * @param {number} [splitNumber = 5] Given approx tick number + * @param {boolean} [fixMin=false] + * @param {boolean} [fixMax=false] + */ + niceExtent: function (splitNumber, fixMin, fixMax) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + if (extent[0] !== 0) { + // Expand extent + var expandSize = extent[0]; + // In the fowllowing case + // Axis has been fixed max 100 + // Plus data are all 100 and axis extent are [100, 100]. + // Extend to the both side will cause expanded max is larger than fixed max. + // So only expand to the smaller side. + if (!fixMax) { + extent[1] += expandSize / 2; + extent[0] -= expandSize / 2; + } + else { + extent[0] -= expandSize / 2; + } + } + else { + extent[1] = 1; + } + } + var span = extent[1] - extent[0]; + // If there are no data and extent are [Infinity, -Infinity] + if (!isFinite(span)) { + extent[0] = 0; + extent[1] = 1; + } + + this.niceTicks(splitNumber); + + // var extent = this._extent; + var interval = this._interval; + + if (!fixMin) { + extent[0] = roundingErrorFix(mathFloor(extent[0] / interval) * interval); + } + if (!fixMax) { + extent[1] = roundingErrorFix(mathCeil(extent[1] / interval) * interval); + } + } + }); + + /** + * @return {module:echarts/scale/Time} + */ + IntervalScale.create = function () { + return new IntervalScale(); + }; + + module.exports = IntervalScale; + + + +/***/ }, +/* 118 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Interval scale + * @module echarts/coord/scale/Time + */ + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + + var IntervalScale = __webpack_require__(117); + + var intervalScaleProto = IntervalScale.prototype; + + var mathCeil = Math.ceil; + var mathFloor = Math.floor; + var ONE_SECOND = 1000; + var ONE_MINUTE = ONE_SECOND * 60; + var ONE_HOUR = ONE_MINUTE * 60; + var ONE_DAY = ONE_HOUR * 24; + + // FIXME 公用? + var bisect = function (a, x, lo, hi) { + while (lo < hi) { + var mid = lo + hi >>> 1; + if (a[mid][2] < x) { + lo = mid + 1; + } + else { + hi = mid; + } + } + return lo; + }; + + /** + * @alias module:echarts/coord/scale/Time + * @constructor + */ + var TimeScale = IntervalScale.extend({ + type: 'time', + + // Overwrite + getLabel: function (val) { + var stepLvl = this._stepLvl; + + var date = new Date(val); + + return formatUtil.formatTime(stepLvl[0], date); + }, + + // Overwrite + niceExtent: function (approxTickNum, fixMin, fixMax) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + // Expand extent + extent[0] -= ONE_DAY; + extent[1] += ONE_DAY; + } + // If there are no data and extent are [Infinity, -Infinity] + if (extent[1] === -Infinity && extent[0] === Infinity) { + var d = new Date(); + extent[1] = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + extent[0] = extent[1] - ONE_DAY; + } + + this.niceTicks(approxTickNum); + + // var extent = this._extent; + var interval = this._interval; + + if (!fixMin) { + extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval); + } + if (!fixMax) { + extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval); + } + }, + + // Overwrite + niceTicks: function (approxTickNum) { + approxTickNum = approxTickNum || 10; + + var extent = this._extent; + var span = extent[1] - extent[0]; + var approxInterval = span / approxTickNum; + var scaleLevelsLen = scaleLevels.length; + var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen); + + var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)]; + var interval = level[2]; + // Same with interval scale if span is much larger than 1 year + if (level[0] === 'year') { + var yearSpan = span / interval; + + // From "Nice Numbers for Graph Labels" of Graphic Gems + // var niceYearSpan = numberUtil.nice(yearSpan, false); + var yearStep = numberUtil.nice(yearSpan / approxTickNum, true); + + interval *= yearStep; + } + + var niceExtent = [ + mathCeil(extent[0] / interval) * interval, + mathFloor(extent[1] / interval) * interval + ]; + + this._stepLvl = level; + // Interval will be used in getTicks + this._interval = interval; + this._niceExtent = niceExtent; + }, + + parse: function (val) { + // val might be float. + return +numberUtil.parseDate(val); + } + }); + + zrUtil.each(['contain', 'normalize'], function (methodName) { + TimeScale.prototype[methodName] = function (val) { + return intervalScaleProto[methodName].call(this, this.parse(val)); + }; + }); + + // Steps from d3 + var scaleLevels = [ + // Format step interval + ['hh:mm:ss', 1, ONE_SECOND], // 1s + ['hh:mm:ss', 5, ONE_SECOND * 5], // 5s + ['hh:mm:ss', 10, ONE_SECOND * 10], // 10s + ['hh:mm:ss', 15, ONE_SECOND * 15], // 15s + ['hh:mm:ss', 30, ONE_SECOND * 30], // 30s + ['hh:mm\nMM-dd',1, ONE_MINUTE], // 1m + ['hh:mm\nMM-dd',5, ONE_MINUTE * 5], // 5m + ['hh:mm\nMM-dd',10, ONE_MINUTE * 10], // 10m + ['hh:mm\nMM-dd',15, ONE_MINUTE * 15], // 15m + ['hh:mm\nMM-dd',30, ONE_MINUTE * 30], // 30m + ['hh:mm\nMM-dd',1, ONE_HOUR], // 1h + ['hh:mm\nMM-dd',2, ONE_HOUR * 2], // 2h + ['hh:mm\nMM-dd',6, ONE_HOUR * 6], // 6h + ['hh:mm\nMM-dd',12, ONE_HOUR * 12], // 12h + ['MM-dd\nyyyy', 1, ONE_DAY], // 1d + ['week', 7, ONE_DAY * 7], // 7d + ['month', 1, ONE_DAY * 31], // 1M + ['quarter', 3, ONE_DAY * 380 / 4], // 3M + ['half-year', 6, ONE_DAY * 380 / 2], // 6M + ['year', 1, ONE_DAY * 380] // 1Y + ]; + + /** + * @return {module:echarts/scale/Time} + */ + TimeScale.create = function () { + return new TimeScale(); + }; + + module.exports = TimeScale; + + +/***/ }, +/* 119 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Log scale + * @module echarts/scale/Log + */ + + + var zrUtil = __webpack_require__(4); + var Scale = __webpack_require__(116); + var numberUtil = __webpack_require__(7); + + // Use some method of IntervalScale + var IntervalScale = __webpack_require__(117); + + var scaleProto = Scale.prototype; + var intervalScaleProto = IntervalScale.prototype; + + var mathFloor = Math.floor; + var mathCeil = Math.ceil; + var mathPow = Math.pow; + + var mathLog = Math.log; + + var LogScale = Scale.extend({ + + type: 'log', + + base: 10, + + /** + * @return {Array.} + */ + getTicks: function () { + return zrUtil.map(intervalScaleProto.getTicks.call(this), function (val) { + return numberUtil.round(mathPow(this.base, val)); + }, this); + }, + + /** + * @param {number} val + * @return {string} + */ + getLabel: intervalScaleProto.getLabel, + + /** + * @param {number} val + * @return {number} + */ + scale: function (val) { + val = scaleProto.scale.call(this, val); + return mathPow(this.base, val); + }, + + /** + * @param {number} start + * @param {number} end + */ + setExtent: function (start, end) { + var base = this.base; + start = mathLog(start) / mathLog(base); + end = mathLog(end) / mathLog(base); + intervalScaleProto.setExtent.call(this, start, end); + }, + + /** + * @return {number} end + */ + getExtent: function () { + var base = this.base; + var extent = scaleProto.getExtent.call(this); + extent[0] = mathPow(base, extent[0]); + extent[1] = mathPow(base, extent[1]); + return extent; + }, + + /** + * @param {Array.} extent + */ + unionExtent: function (extent) { + var base = this.base; + extent[0] = mathLog(extent[0]) / mathLog(base); + extent[1] = mathLog(extent[1]) / mathLog(base); + scaleProto.unionExtent.call(this, extent); + }, + + /** + * Update interval and extent of intervals for nice ticks + * @param {number} [approxTickNum = 10] Given approx tick number + */ + niceTicks: function (approxTickNum) { + approxTickNum = approxTickNum || 10; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (span === Infinity || span <= 0) { + return; + } + + var interval = numberUtil.quantity(span); + var err = approxTickNum / span * interval; + + // Filter ticks to get closer to the desired count. + if (err <= 0.5) { + interval *= 10; + } + + // Interval should be integer + while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { + interval *= 10; + } + + var niceExtent = [ + numberUtil.round(mathCeil(extent[0] / interval) * interval), + numberUtil.round(mathFloor(extent[1] / interval) * interval) + ]; + + this._interval = interval; + this._niceExtent = niceExtent; + }, + + /** + * Nice extent. + * @param {number} [approxTickNum = 10] Given approx tick number + * @param {boolean} [fixMin=false] + * @param {boolean} [fixMax=false] + */ + niceExtent: intervalScaleProto.niceExtent + }); + + zrUtil.each(['contain', 'normalize'], function (methodName) { + LogScale.prototype[methodName] = function (val) { + val = mathLog(val) / mathLog(this.base); + return scaleProto[methodName].call(this, val); + }; + }); + + LogScale.create = function () { + return new LogScale(); + }; + + module.exports = LogScale; + + +/***/ }, +/* 120 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Cartesian = __webpack_require__(121); + + function Cartesian2D(name) { + + Cartesian.call(this, name); + } + + Cartesian2D.prototype = { + + constructor: Cartesian2D, + + type: 'cartesian2d', + + /** + * @type {Array.} + * @readOnly + */ + dimensions: ['x', 'y'], + + /** + * Base axis will be used on stacking. + * + * @return {module:echarts/coord/cartesian/Axis2D} + */ + getBaseAxis: function () { + return this.getAxesByScale('ordinal')[0] + || this.getAxesByScale('time')[0] + || this.getAxis('x'); + }, + + /** + * If contain point + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var axisX = this.getAxis('x'); + var axisY = this.getAxis('y'); + return axisX.contain(axisX.toLocalCoord(point[0])) + && axisY.contain(axisY.toLocalCoord(point[1])); + }, + + /** + * If contain data + * @param {Array.} data + * @return {boolean} + */ + containData: function (data) { + return this.getAxis('x').containData(data[0]) + && this.getAxis('y').containData(data[1]); + }, + + /** + * Convert series data to an array of points + * @param {module:echarts/data/List} data + * @param {boolean} stack + * @return {Array} + * Return array of points. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data, stack) { + return data.mapArray(['x', 'y'], function (x, y) { + return this.dataToPoint([x, y]); + }, stack, this); + }, + + /** + * @param {Array.} data + * @param {boolean} [clamp=false] + * @return {Array.} + */ + dataToPoint: function (data, clamp) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + return [ + xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)), + yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp)) + ]; + }, + + /** + * @param {Array.} point + * @param {boolean} [clamp=false] + * @return {Array.} + */ + pointToData: function (point, clamp) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + return [ + xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp), + yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp) + ]; + }, + + /** + * Get other axis + * @param {module:echarts/coord/cartesian/Axis2D} axis + */ + getOtherAxis: function (axis) { + return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); + } + }; + + zrUtil.inherits(Cartesian2D, Cartesian); + + module.exports = Cartesian2D; + + +/***/ }, +/* 121 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Cartesian coordinate system + * @module echarts/coord/Cartesian + * + */ + + + var zrUtil = __webpack_require__(4); + + function dimAxisMapper(dim) { + return this._axes[dim]; + } + + /** + * @alias module:echarts/coord/Cartesian + * @constructor + */ + var Cartesian = function (name) { + this._axes = {}; + + this._dimList = []; + + /** + * @type {string} + */ + this.name = name || ''; + }; + + Cartesian.prototype = { + + constructor: Cartesian, + + type: 'cartesian', + + /** + * Get axis + * @param {number|string} dim + * @return {module:echarts/coord/Cartesian~Axis} + */ + getAxis: function (dim) { + return this._axes[dim]; + }, + + /** + * Get axes list + * @return {Array.} + */ + getAxes: function () { + return zrUtil.map(this._dimList, dimAxisMapper, this); + }, + + /** + * Get axes list by given scale type + */ + getAxesByScale: function (scaleType) { + scaleType = scaleType.toLowerCase(); + return zrUtil.filter( + this.getAxes(), + function (axis) { + return axis.scale.type === scaleType; + } + ); + }, + + /** + * Add axis + * @param {module:echarts/coord/Cartesian.Axis} + */ + addAxis: function (axis) { + var dim = axis.dim; + + this._axes[dim] = axis; + + this._dimList.push(dim); + }, + + /** + * Convert data to coord in nd space + * @param {Array.|Object.} val + * @return {Array.|Object.} + */ + dataToCoord: function (val) { + return this._dataCoordConvert(val, 'dataToCoord'); + }, + + /** + * Convert coord in nd space to data + * @param {Array.|Object.} val + * @return {Array.|Object.} + */ + coordToData: function (val) { + return this._dataCoordConvert(val, 'coordToData'); + }, + + _dataCoordConvert: function (input, method) { + var dimList = this._dimList; + + var output = input instanceof Array ? [] : {}; + + for (var i = 0; i < dimList.length; i++) { + var dim = dimList[i]; + var axis = this._axes[dim]; + + output[dim] = axis[method](input[dim]); + } + + return output; + } + }; + + module.exports = Cartesian; + + +/***/ }, +/* 122 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisLabelInterval = __webpack_require__(124); + + /** + * Extend axis 2d + * @constructor module:echarts/coord/cartesian/Axis2D + * @extends {module:echarts/coord/cartesian/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var Axis2D = function (dim, scale, coordExtent, axisType, position) { + Axis.call(this, dim, scale, coordExtent); + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * Axis position + * - 'top' + * - 'bottom' + * - 'left' + * - 'right' + */ + this.position = position || 'bottom'; + }; + + Axis2D.prototype = { + + constructor: Axis2D, + + /** + * Index of axis, can be used as key + */ + index: 0, + /** + * If axis is on the zero position of the other axis + * @type {boolean} + */ + onZero: false, + + /** + * Axis model + * @param {module:echarts/coord/cartesian/AxisModel} + */ + model: null, + + isHorizontal: function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + }, + + getGlobalExtent: function () { + var ret = this.getExtent(); + ret[0] = this.toGlobalCoord(ret[0]); + ret[1] = this.toGlobalCoord(ret[1]); + return ret; + }, + + /** + * @return {number} + */ + getLabelInterval: function () { + var labelInterval = this._labelInterval; + if (!labelInterval) { + labelInterval = this._labelInterval = axisLabelInterval(this); + } + return labelInterval; + }, + + /** + * If label is ignored. + * Automatically used when axis is category and label can not be all shown + * @param {number} idx + * @return {boolean} + */ + isLabelIgnored: function (idx) { + if (this.type === 'category') { + var labelInterval = this.getLabelInterval(); + return ((typeof labelInterval === 'function') + && !labelInterval(idx, this.scale.getLabel(idx))) + || idx % (labelInterval + 1); + } + }, + + /** + * Transform global coord to local coord, + * i.e. var localCoord = axis.toLocalCoord(80); + * designate by module:echarts/coord/cartesian/Grid. + * @type {Function} + */ + toLocalCoord: null, + + /** + * Transform global coord to local coord, + * i.e. var globalCoord = axis.toLocalCoord(40); + * designate by module:echarts/coord/cartesian/Grid. + * @type {Function} + */ + toGlobalCoord: null + + }; + zrUtil.inherits(Axis2D, Axis); + + module.exports = Axis2D; + + +/***/ }, +/* 123 */ +/***/ function(module, exports, __webpack_require__) { + + + + var numberUtil = __webpack_require__(7); + var linearMap = numberUtil.linearMap; + var zrUtil = __webpack_require__(4); + + function fixExtentWithBands(extent, nTick) { + var size = extent[1] - extent[0]; + var len = nTick; + var margin = size / len / 2; + extent[0] += margin; + extent[1] -= margin; + } + + var normalizedExtent = [0, 1]; + /** + * @name module:echarts/coord/CartesianAxis + * @constructor + */ + var Axis = function (dim, scale, extent) { + + /** + * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius' + * @type {string} + */ + this.dim = dim; + + /** + * Axis scale + * @type {module:echarts/coord/scale/*} + */ + this.scale = scale; + + /** + * @type {Array.} + * @private + */ + this._extent = extent || [0, 0]; + + /** + * @type {boolean} + */ + this.inverse = false; + + /** + * Usually true when axis has a ordinal scale + * @type {boolean} + */ + this.onBand = false; + }; + + Axis.prototype = { + + constructor: Axis, + + /** + * If axis extent contain given coord + * @param {number} coord + * @return {boolean} + */ + contain: function (coord) { + var extent = this._extent; + var min = Math.min(extent[0], extent[1]); + var max = Math.max(extent[0], extent[1]); + return coord >= min && coord <= max; + }, + + /** + * If axis extent contain given data + * @param {number} data + * @return {boolean} + */ + containData: function (data) { + return this.contain(this.dataToCoord(data)); + }, + + /** + * Get coord extent. + * @return {Array.} + */ + getExtent: function () { + var ret = this._extent.slice(); + return ret; + }, + + /** + * Get precision used for formatting + * @param {Array.} [dataExtent] + * @return {number} + */ + getPixelPrecision: function (dataExtent) { + return numberUtil.getPixelPrecision( + dataExtent || this.scale.getExtent(), + this._extent + ); + }, + + /** + * Set coord extent + * @param {number} start + * @param {number} end + */ + setExtent: function (start, end) { + var extent = this._extent; + extent[0] = start; + extent[1] = end; + }, + + /** + * Convert data to coord. Data is the rank if it has a ordinal scale + * @param {number} data + * @param {boolean} clamp + * @return {number} + */ + dataToCoord: function (data, clamp) { + var extent = this._extent; + var scale = this.scale; + data = scale.normalize(data); + + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + + return linearMap(data, normalizedExtent, extent, clamp); + }, + + /** + * Convert coord to data. Data is the rank if it has a ordinal scale + * @param {number} coord + * @param {boolean} clamp + * @return {number} + */ + coordToData: function (coord, clamp) { + var extent = this._extent; + var scale = this.scale; + + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + + var t = linearMap(coord, extent, normalizedExtent, clamp); + + return this.scale.scale(t); + }, + /** + * @return {Array.} + */ + getTicksCoords: function (alignWithLabel) { + if (this.onBand && !alignWithLabel) { + var bands = this.getBands(); + var coords = []; + for (var i = 0; i < bands.length; i++) { + coords.push(bands[i][0]); + } + if (bands[i - 1]) { + coords.push(bands[i - 1][1]); + } + return coords; + } + else { + return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this); + } + }, + + /** + * Coords of labels are on the ticks or on the middle of bands + * @return {Array.} + */ + getLabelsCoords: function () { + return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this); + }, + + /** + * Get bands. + * + * If axis has labels [1, 2, 3, 4]. Bands on the axis are + * |---1---|---2---|---3---|---4---|. + * + * @return {Array} + */ + // FIXME Situation when labels is on ticks + getBands: function () { + var extent = this.getExtent(); + var bands = []; + var len = this.scale.count(); + var start = extent[0]; + var end = extent[1]; + var span = end - start; + + for (var i = 0; i < len; i++) { + bands.push([ + span * i / len + start, + span * (i + 1) / len + start + ]); + } + return bands; + }, + + /** + * Get width of band + * @return {number} + */ + getBandWidth: function () { + var axisExtent = this._extent; + var dataExtent = this.scale.getExtent(); + + var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); + // Fix #2728, avoid NaN when only one data. + len === 0 && (len = 1); + + var size = Math.abs(axisExtent[1] - axisExtent[0]); + + return Math.abs(size) / len; + } + }; + + module.exports = Axis; + + +/***/ }, +/* 124 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Helper function for axisLabelInterval calculation + */ + + + + var zrUtil = __webpack_require__(4); + var axisHelper = __webpack_require__(114); + + module.exports = function (axis) { + var axisModel = axis.model; + var labelModel = axisModel.getModel('axisLabel'); + var labelInterval = labelModel.get('interval'); + if (!(axis.type === 'category' && labelInterval === 'auto')) { + return labelInterval === 'auto' ? 0 : labelInterval; + } + + return axisHelper.getAxisLabelInterval( + zrUtil.map(axis.scale.getTicks(), axis.dataToCoord, axis), + axisModel.getFormattedLabels(), + labelModel.getModel('textStyle').getFont(), + axis.isHorizontal() + ); + }; + + +/***/ }, +/* 125 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Grid 是在有直角坐标系的时候必须要存在的 + // 所以这里也要被 Cartesian2D 依赖 + + + __webpack_require__(126); + var ComponentModel = __webpack_require__(19); + + module.exports = ComponentModel.extend({ + + type: 'grid', + + dependencies: ['xAxis', 'yAxis'], + + layoutMode: 'box', + + /** + * @type {module:echarts/coord/cartesian/Grid} + */ + coordinateSystem: null, + + defaultOption: { + show: false, + zlevel: 0, + z: 0, + left: '10%', + top: 60, + right: '10%', + bottom: 60, + // If grid size contain label + containLabel: false, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 1, + borderColor: '#ccc' + } + }); + + +/***/ }, +/* 126 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var ComponentModel = __webpack_require__(19); + var zrUtil = __webpack_require__(4); + var axisModelCreator = __webpack_require__(127); + + var AxisModel = ComponentModel.extend({ + + type: 'cartesian2dAxis', + + /** + * @type {module:echarts/coord/cartesian/Axis2D} + */ + axis: null, + + /** + * @override + */ + init: function () { + AxisModel.superApply(this, 'init', arguments); + this._resetRange(); + }, + + /** + * @override + */ + mergeOption: function () { + AxisModel.superApply(this, 'mergeOption', arguments); + this._resetRange(); + }, + + /** + * @override + */ + restoreData: function () { + AxisModel.superApply(this, 'restoreData', arguments); + this._resetRange(); + }, + + /** + * @public + * @param {number} rangeStart + * @param {number} rangeEnd + */ + setRange: function (rangeStart, rangeEnd) { + this.option.rangeStart = rangeStart; + this.option.rangeEnd = rangeEnd; + }, + + /** + * @public + * @return {Array.} + */ + getMin: function () { + var option = this.option; + return option.rangeStart != null ? option.rangeStart : option.min; + }, + + /** + * @public + * @return {Array.} + */ + getMax: function () { + var option = this.option; + return option.rangeEnd != null ? option.rangeEnd : option.max; + }, + + /** + * @public + * @return {boolean} + */ + getNeedCrossZero: function () { + var option = this.option; + return (option.rangeStart != null || option.rangeEnd != null) + ? false : !option.scale; + }, + + /** + * @return {module:echarts/model/Model} + */ + findGridModel: function () { + return this.ecModel.queryComponents({ + mainType: 'grid', + index: this.get('gridIndex'), + id: this.get('gridId') + })[0]; + }, + + /** + * @private + */ + _resetRange: function () { + // rangeStart and rangeEnd is readonly. + this.option.rangeStart = this.option.rangeEnd = null; + } + + }); + + function getAxisType(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); + } + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + var extraOption = { + // gridIndex: 0, + // gridId: '', + + // Offset is for multiple axis on the same position + offset: 0 + }; + + axisModelCreator('x', AxisModel, getAxisType, extraOption); + axisModelCreator('y', AxisModel, getAxisType, extraOption); + + module.exports = AxisModel; + + +/***/ }, +/* 127 */ +/***/ function(module, exports, __webpack_require__) { + + + + var axisDefault = __webpack_require__(128); + var zrUtil = __webpack_require__(4); + var ComponentModel = __webpack_require__(19); + var layout = __webpack_require__(21); + + // FIXME axisType is fixed ? + var AXIS_TYPES = ['value', 'category', 'time', 'log']; + + /** + * Generate sub axis model class + * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel' + * @param {module:echarts/model/Component} BaseAxisModelClass + * @param {Function} axisTypeDefaulter + * @param {Object} [extraDefaultOption] + */ + module.exports = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) { + + zrUtil.each(AXIS_TYPES, function (axisType) { + + BaseAxisModelClass.extend({ + + type: axisName + 'Axis.' + axisType, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? layout.getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + zrUtil.merge(option, themeModel.get(axisType + 'Axis')); + zrUtil.merge(option, this.getDefaultOption()); + + option.type = axisTypeDefaulter(axisName, option); + + if (layoutMode) { + layout.mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + defaultOption: zrUtil.mergeAll( + [ + {}, + axisDefault[axisType + 'Axis'], + extraDefaultOption + ], + true + ) + }); + }); + + ComponentModel.registerSubTypeDefaulter( + axisName + 'Axis', + zrUtil.curry(axisTypeDefaulter, axisName) + ); + }; + + +/***/ }, +/* 128 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var defaultOption = { + show: true, + zlevel: 0, // 一级层叠 + z: 0, // 二级层叠 + // 反向坐标轴 + inverse: false, + + // 坐标轴名字,默认为空 + name: '', + // 坐标轴名字位置,支持'start' | 'middle' | 'end' + nameLocation: 'end', + // 坐标轴名字旋转,degree。 + nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'. + nameTruncate: { + maxWidth: null, + ellipsis: '...', + placeholder: '.' + }, + // 坐标轴文字样式,默认取全局样式 + nameTextStyle: {}, + // 文字与轴线距离 + nameGap: 15, + + silent: false, // Default false to support tooltip. + triggerEvent: false, // Default false to avoid legacy user event listener fail. + + tooltip: { + show: false + }, + + // 坐标轴线 + axisLine: { + // 默认显示,属性show控制显示与否 + show: true, + onZero: true, + // 属性lineStyle控制线条样式 + lineStyle: { + color: '#333', + width: 1, + type: 'solid' + } + }, + // 坐标轴小标记 + axisTick: { + // 属性show控制显示与否,默认显示 + show: true, + // 控制小标记是否在grid里 + inside: false, + // 属性length控制线长 + length: 5, + // 属性lineStyle控制线条样式 + lineStyle: { + width: 1 + } + }, + // 坐标轴文本标签,详见axis.axisLabel + axisLabel: { + show: true, + // 控制文本标签是否在grid里 + inside: false, + rotate: 0, + margin: 8, + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + fontSize: 12 + } + }, + // 分隔线 + splitLine: { + // 默认显示,属性show控制显示与否 + show: true, + // 属性lineStyle(详见lineStyle)控制线条样式 + lineStyle: { + color: ['#ccc'], + width: 1, + type: 'solid' + } + }, + // 分隔区域 + splitArea: { + // 默认不显示,属性show控制显示与否 + show: false, + // 属性areaStyle(详见areaStyle)控制区域样式 + areaStyle: { + color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)'] + } + } + }; + + var categoryAxis = zrUtil.merge({ + // 类目起始和结束两端空白策略 + boundaryGap: true, + // splitArea: { + // show: false + // }, + splitLine: { + show: false + }, + // 坐标轴小标记 + axisTick: { + // If tick is align with label when boundaryGap is true + // Default with axisTick + alignWithLabel: false, + interval: 'auto' + }, + // 坐标轴文本标签,详见axis.axisLabel + axisLabel: { + interval: 'auto' + } + }, defaultOption); + + var valueAxis = zrUtil.merge({ + // 数值起始和结束两端空白策略 + boundaryGap: [0, 0], + // 最小值, 设置成 'dataMin' 则从数据中计算最小值 + // min: null, + // 最大值,设置成 'dataMax' 则从数据中计算最大值 + // max: null, + // Readonly prop, specifies start value of the range when using data zoom. + // rangeStart: null + // Readonly prop, specifies end value of the range when using data zoom. + // rangeEnd: null + // 脱离0值比例,放大聚焦到最终_min,_max区间 + // scale: false, + // 分割段数,默认为5 + splitNumber: 5 + // Minimum interval + // minInterval: null + }, defaultOption); + + // FIXME + var timeAxis = zrUtil.defaults({ + scale: true, + min: 'dataMin', + max: 'dataMax' + }, valueAxis); + var logAxis = zrUtil.defaults({ + logBase: 10 + }, valueAxis); + logAxis.scale = true; + + module.exports = { + categoryAxis: categoryAxis, + valueAxis: valueAxis, + timeAxis: timeAxis, + logAxis: logAxis + }; + + +/***/ }, +/* 129 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var axisHelper = __webpack_require__(114); + + function getName(obj) { + if (zrUtil.isObject(obj) && obj.value != null) { + return obj.value; + } + else { + return obj; + } + } + /** + * Get categories + */ + function getCategories() { + return this.get('type') === 'category' + && zrUtil.map(this.get('data'), getName); + } + + /** + * Format labels + * @return {Array.} + */ + function getFormattedLabels() { + return axisHelper.getFormattedLabels( + this.axis, + this.get('axisLabel.formatter') + ); + } + + module.exports = { + + getFormattedLabels: getFormattedLabels, + + getCategories: getCategories + }; + + +/***/ }, +/* 130 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // TODO boundaryGap + + + __webpack_require__(126); + + __webpack_require__(131); + + +/***/ }, +/* 131 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var AxisBuilder = __webpack_require__(132); + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick; + var getInterval = AxisBuilder.getInterval; + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + var selfBuilderAttrs = [ + 'splitArea', 'splitLine' + ]; + + // function getAlignWithLabel(model, axisModel) { + // var alignWithLabel = model.get('alignWithLabel'); + // if (alignWithLabel === 'auto') { + // alignWithLabel = axisModel.get('axisTick.alignWithLabel'); + // } + // return alignWithLabel; + // } + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'axis', + + render: function (axisModel, ecModel) { + + this.group.removeAll(); + + var oldAxisGroup = this._axisGroup; + this._axisGroup = new graphic.Group(); + + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var gridModel = axisModel.findGridModel(); + + var layout = layoutAxis(gridModel, axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + zrUtil.each(selfBuilderAttrs, function (name) { + if (axisModel.get(name + '.show')) { + this['_' + name](axisModel, gridModel, layout.labelInterval); + } + }, this); + + graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel); + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {number|Function} labelInterval + * @private + */ + _splitLine: function (axisModel, gridModel, labelInterval) { + var axis = axisModel.axis; + + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + + var lineInterval = getInterval(splitLineModel, labelInterval); + + lineColors = zrUtil.isArray(lineColors) ? lineColors : [lineColors]; + + var gridRect = gridModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords( + // splitLineModel.get('alignWithLabel') + ); + var ticks = axis.scale.getTicks(); + + var p1 = []; + var p2 = []; + // Simple optimization + // Batching the lines if color are the same + var lineStyle = lineStyleModel.getLineStyle(); + for (var i = 0; i < ticksCoords.length; i++) { + if (ifIgnoreOnTick(axis, i, lineInterval)) { + continue; + } + + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } + else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + + var colorIndex = (lineCount++) % lineColors.length; + this._axisGroup.add(new graphic.Line(graphic.subPixelOptimizeLine({ + anid: 'line_' + ticks[i], + + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: zrUtil.defaults({ + stroke: lineColors[colorIndex] + }, lineStyle), + silent: true + }))); + } + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {number|Function} labelInterval + * @private + */ + _splitArea: function (axisModel, gridModel, labelInterval) { + var axis = axisModel.axis; + + var splitAreaModel = axisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + + var gridRect = gridModel.coordinateSystem.getRect(); + + var ticksCoords = axis.getTicksCoords( + // splitAreaModel.get('alignWithLabel') + ); + var ticks = axis.scale.getTicks(); + + var prevX = axis.toGlobalCoord(ticksCoords[0]); + var prevY = axis.toGlobalCoord(ticksCoords[0]); + + var count = 0; + + var areaInterval = getInterval(splitAreaModel, labelInterval); + + var areaStyle = areaStyleModel.getAreaStyle(); + areaColors = zrUtil.isArray(areaColors) ? areaColors : [areaColors]; + + for (var i = 1; i < ticksCoords.length; i++) { + if (ifIgnoreOnTick(axis, i, areaInterval)) { + continue; + } + + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + + var x; + var y; + var width; + var height; + if (axis.isHorizontal()) { + x = prevX; + y = gridRect.y; + width = tickCoord - x; + height = gridRect.height; + } + else { + x = gridRect.x; + y = prevY; + width = gridRect.width; + height = tickCoord - y; + } + + var colorIndex = (count++) % areaColors.length; + this._axisGroup.add(new graphic.Rect({ + anid: 'area_' + ticks[i], + + shape: { + x: x, + y: y, + width: width, + height: height + }, + style: zrUtil.defaults({ + fill: areaColors[colorIndex] + }, areaStyle), + silent: true + })); + + prevX = x + width; + prevY = y + height; + } + } + }); + + AxisView.extend({ + type: 'xAxis' + }); + AxisView.extend({ + type: 'yAxis' + }); + + /** + * @inner + */ + function layoutAxis(gridModel, axisModel) { + var grid = gridModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + + var rawAxisPosition = axis.position; + var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition; + var axisDim = axis.dim; + + // [left, right, top, bottom] + var rect = grid.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + + var axisOffset = axisModel.get('offset') || 0; + + var posMap = { + x: { top: rectBound[2] - axisOffset, bottom: rectBound[3] + axisOffset }, + y: { left: rectBound[0] - axisOffset, right: rectBound[1] + axisOffset } + }; + + posMap.x.onZero = Math.max(Math.min(getZero('y'), posMap.x.bottom), posMap.x.top); + posMap.y.onZero = Math.max(Math.min(getZero('x'), posMap.y.right), posMap.y.left); + + function getZero(dim, val) { + var theAxis = grid.getAxis(dim); + return theAxis.toGlobalCoord(theAxis.dataToCoord(0)); + } + + // Axis position + layout.position = [ + axisDim === 'y' ? posMap.y[axisPosition] : rectBound[0], + axisDim === 'x' ? posMap.x[axisPosition] : rectBound[3] + ]; + + // Axis rotation + layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); + + // Tick and label direction, x y is axisDim + var dirMap = {top: -1, bottom: 1, left: -1, right: 1}; + + layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition]; + if (axis.onZero) { + layout.labelOffset = posMap[axisDim][rawAxisPosition] - posMap[axisDim].onZero; + } + + if (axisModel.getModel('axisTick').get('inside')) { + layout.tickDirection = -layout.tickDirection; + } + if (axisModel.getModel('axisLabel').get('inside')) { + layout.labelDirection = -layout.labelDirection; + } + + // Special label rotation + var labelRotation = axisModel.getModel('axisLabel').get('rotate'); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + + // label interval when auto mode. + layout.labelInterval = axis.getLabelInterval(); + + // Over splitLine and splitArea + layout.z2 = 1; + + return layout; + } + + +/***/ }, +/* 132 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + var numberUtil = __webpack_require__(7); + var remRadian = numberUtil.remRadian; + var isRadianAroundZero = numberUtil.isRadianAroundZero; + var vec2 = __webpack_require__(10); + var v2ApplyTransform = vec2.applyTransform; + var retrieve = zrUtil.retrieve; + + var PI = Math.PI; + + function makeAxisEventDataBase(axisModel) { + var eventData = { + componentType: axisModel.mainType + }; + eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex; + return eventData; + } + + /** + * A final axis is translated and rotated from a "standard axis". + * So opt.position and opt.rotation is required. + * + * A standard axis is and axis from [0, 0] to [0, axisExtent[1]], + * for example: (0, 0) ------------> (0, 50) + * + * nameDirection or tickDirection or labelDirection is 1 means tick + * or label is below the standard axis, whereas is -1 means above + * the standard axis. labelOffset means offset between label and axis, + * which is useful when 'onZero', where axisLabel is in the grid and + * label in outside grid. + * + * Tips: like always, + * positive rotation represents anticlockwise, and negative rotation + * represents clockwise. + * The direction of position coordinate is the same as the direction + * of screen coordinate. + * + * Do not need to consider axis 'inverse', which is auto processed by + * axis extent. + * + * @param {module:zrender/container/Group} group + * @param {Object} axisModel + * @param {Object} opt Standard axis parameters. + * @param {Array.} opt.position [x, y] + * @param {number} opt.rotation by radian + * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle'. + * @param {number} [opt.tickDirection=1] 1 or -1 + * @param {number} [opt.labelDirection=1] 1 or -1 + * @param {number} [opt.labelOffset=0] Usefull when onZero. + * @param {string} [opt.axisLabelShow] default get from axisModel. + * @param {string} [opt.axisName] default get from axisModel. + * @param {number} [opt.axisNameAvailableWidth] + * @param {number} [opt.labelRotation] by degree, default get from axisModel. + * @param {number} [opt.labelInterval] Default label interval when label + * interval from model is null or 'auto'. + * @param {number} [opt.strokeContainThreshold] Default label interval when label + */ + var AxisBuilder = function (axisModel, opt) { + + /** + * @readOnly + */ + this.opt = opt; + + /** + * @readOnly + */ + this.axisModel = axisModel; + + // Default value + zrUtil.defaults( + opt, + { + labelOffset: 0, + nameDirection: 1, + tickDirection: 1, + labelDirection: 1, + silent: true + } + ); + + /** + * @readOnly + */ + this.group = new graphic.Group(); + + // FIXME Not use a seperate text group? + var dumbGroup = new graphic.Group({ + position: opt.position.slice(), + rotation: opt.rotation + }); + + // this.group.add(dumbGroup); + // this._dumbGroup = dumbGroup; + + dumbGroup.updateTransform(); + this._transform = dumbGroup.transform; + + this._dumbGroup = dumbGroup; + }; + + AxisBuilder.prototype = { + + constructor: AxisBuilder, + + hasBuilder: function (name) { + return !!builders[name]; + }, + + add: function (name) { + builders[name].call(this); + }, + + getGroup: function () { + return this.group; + } + + }; + + var builders = { + + /** + * @private + */ + axisLine: function () { + var opt = this.opt; + var axisModel = this.axisModel; + + if (!axisModel.get('axisLine.show')) { + return; + } + + var extent = this.axisModel.axis.getExtent(); + + var matrix = this._transform; + var pt1 = [extent[0], 0]; + var pt2 = [extent[1], 0]; + if (matrix) { + v2ApplyTransform(pt1, pt1, matrix); + v2ApplyTransform(pt2, pt2, matrix); + } + + this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({ + + // Id for animation + anid: 'line', + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: zrUtil.extend( + {lineCap: 'round'}, + axisModel.getModel('axisLine.lineStyle').getLineStyle() + ), + strokeContainThreshold: opt.strokeContainThreshold || 5, + silent: true, + z2: 1 + }))); + }, + + /** + * @private + */ + axisTick: function () { + var axisModel = this.axisModel; + + if (!axisModel.get('axisTick.show')) { + return; + } + + var axis = axisModel.axis; + var tickModel = axisModel.getModel('axisTick'); + var opt = this.opt; + + var lineStyleModel = tickModel.getModel('lineStyle'); + var tickLen = tickModel.get('length'); + + var tickInterval = getInterval(tickModel, opt.labelInterval); + var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel')); + var ticks = axis.scale.getTicks(); + + var pt1 = []; + var pt2 = []; + var matrix = this._transform; + + for (var i = 0; i < ticksCoords.length; i++) { + // Only ordinal scale support tick interval + if (ifIgnoreOnTick(axis, i, tickInterval)) { + continue; + } + + var tickCoord = ticksCoords[i]; + + pt1[0] = tickCoord; + pt1[1] = 0; + pt2[0] = tickCoord; + pt2[1] = opt.tickDirection * tickLen; + + if (matrix) { + v2ApplyTransform(pt1, pt1, matrix); + v2ApplyTransform(pt2, pt2, matrix); + } + // Tick line, Not use group transform to have better line draw + this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({ + + // Id for animation + anid: 'tick_' + ticks[i], + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: zrUtil.defaults( + lineStyleModel.getLineStyle(), + { + stroke: axisModel.get('axisLine.lineStyle.color') + } + ), + z2: 2, + silent: true + }))); + } + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @private + */ + axisLabel: function () { + var opt = this.opt; + var axisModel = this.axisModel; + var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show')); + + if (!show) { + return; + } + + var axis = axisModel.axis; + var labelModel = axisModel.getModel('axisLabel'); + var textStyleModel = labelModel.getModel('textStyle'); + var labelMargin = labelModel.get('margin'); + var ticks = axis.scale.getTicks(); + var labels = axisModel.getFormattedLabels(); + + // Special label rotate. + var labelRotation = retrieve(opt.labelRotation, labelModel.get('rotate')) || 0; + // To radian. + labelRotation = labelRotation * PI / 180; + + var labelLayout = innerTextLayout(opt, labelRotation, opt.labelDirection); + var categoryData = axisModel.get('data'); + + var textEls = []; + var silent = isSilent(axisModel); + var triggerEvent = axisModel.get('triggerEvent'); + + for (var i = 0; i < ticks.length; i++) { + if (ifIgnoreOnTick(axis, i, opt.labelInterval)) { + continue; + } + + var itemTextStyleModel = textStyleModel; + if (categoryData && categoryData[i] && categoryData[i].textStyle) { + itemTextStyleModel = new Model( + categoryData[i].textStyle, textStyleModel, axisModel.ecModel + ); + } + var textColor = itemTextStyleModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'); + + var tickCoord = axis.dataToCoord(ticks[i]); + var pos = [ + tickCoord, + opt.labelOffset + opt.labelDirection * labelMargin + ]; + var labelBeforeFormat = axis.scale.getLabel(ticks[i]); + + var textEl = new graphic.Text({ + + // Id for animation + anid: 'label_' + ticks[i], + + style: { + text: labels[i], + textAlign: itemTextStyleModel.get('align', true) || labelLayout.textAlign, + textVerticalAlign: itemTextStyleModel.get('baseline', true) || labelLayout.verticalAlign, + textFont: itemTextStyleModel.getFont(), + fill: typeof textColor === 'function' ? textColor(labelBeforeFormat) : textColor + }, + position: pos, + rotation: labelLayout.rotation, + silent: silent, + z2: 10 + }); + + // Pack data for mouse event + if (triggerEvent) { + textEl.eventData = makeAxisEventDataBase(axisModel); + textEl.eventData.targetType = 'axisLabel'; + textEl.eventData.value = labelBeforeFormat; + } + + + // FIXME + this._dumbGroup.add(textEl); + textEl.updateTransform(); + + textEls.push(textEl); + this.group.add(textEl); + + textEl.decomposeTransform(); + } + + function isTwoLabelOverlapped(current, next) { + var firstRect = current && current.getBoundingRect().clone(); + var nextRect = next && next.getBoundingRect().clone(); + if (firstRect && nextRect) { + firstRect.applyTransform(current.getLocalTransform()); + nextRect.applyTransform(next.getLocalTransform()); + return firstRect.intersect(nextRect); + } + } + if (axis.type !== 'category') { + // If min or max are user set, we need to check + // If the tick on min(max) are overlap on their neighbour tick + // If they are overlapped, we need to hide the min(max) tick label + if (axisModel.getMin ? axisModel.getMin() : axisModel.get('min')) { + var firstLabel = textEls[0]; + var nextLabel = textEls[1]; + if (isTwoLabelOverlapped(firstLabel, nextLabel)) { + firstLabel.ignore = true; + } + } + if (axisModel.getMax ? axisModel.getMax() : axisModel.get('max')) { + var lastLabel = textEls[textEls.length - 1]; + var prevLabel = textEls[textEls.length - 2]; + if (isTwoLabelOverlapped(prevLabel, lastLabel)) { + lastLabel.ignore = true; + } + } + } + }, + + /** + * @private + */ + axisName: function () { + var opt = this.opt; + var axisModel = this.axisModel; + var name = retrieve(opt.axisName, axisModel.get('name')); + + if (!name) { + return; + } + + var nameLocation = axisModel.get('nameLocation'); + var nameDirection = opt.nameDirection; + var textStyleModel = axisModel.getModel('nameTextStyle'); + var gap = axisModel.get('nameGap') || 0; + + var extent = this.axisModel.axis.getExtent(); + var gapSignal = extent[0] > extent[1] ? -1 : 1; + var pos = [ + nameLocation === 'start' + ? extent[0] - gapSignal * gap + : nameLocation === 'end' + ? extent[1] + gapSignal * gap + : (extent[0] + extent[1]) / 2, // 'middle' + // Reuse labelOffset. + nameLocation === 'middle' ? opt.labelOffset + nameDirection * gap : 0 + ]; + + var labelLayout; + + var nameRotation = axisModel.get('nameRotate'); + if (nameRotation != null) { + nameRotation = nameRotation * PI / 180; // To radian. + } + + var axisNameAvailableWidth; + + if (nameLocation === 'middle') { + labelLayout = innerTextLayout( + opt, + nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis. + nameDirection + ); + } + else { + labelLayout = endTextLayout( + opt, nameLocation, nameRotation || 0, extent + ); + + axisNameAvailableWidth = opt.axisNameAvailableWidth; + if (axisNameAvailableWidth != null) { + axisNameAvailableWidth = Math.abs( + axisNameAvailableWidth / Math.sin(labelLayout.rotation) + ); + !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null); + } + } + + var textFont = textStyleModel.getFont(); + + var truncateOpt = axisModel.get('nameTruncate', true) || {}; + var ellipsis = truncateOpt.ellipsis; + var maxWidth = retrieve(truncateOpt.maxWidth, axisNameAvailableWidth); + var truncatedText = (ellipsis != null && maxWidth != null) + ? formatUtil.truncateText( + name, maxWidth, textFont, ellipsis, + {minChar: 2, placeholder: truncateOpt.placeholder} + ) + : name; + + var tooltipOpt = axisModel.get('tooltip', true); + + var mainType = axisModel.mainType; + var formatterParams = { + componentType: mainType, + name: name, + $vars: ['name'] + }; + formatterParams[mainType + 'Index'] = axisModel.componentIndex; + + var textEl = new graphic.Text({ + + // Id for animation + anid: 'name', + + __fullText: name, + __truncatedText: truncatedText, + + style: { + text: truncatedText, + textFont: textFont, + fill: textStyleModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'), + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign + }, + position: pos, + rotation: labelLayout.rotation, + silent: isSilent(axisModel), + z2: 1, + tooltip: (tooltipOpt && tooltipOpt.show) + ? zrUtil.extend({ + content: name, + formatter: function () { + return name; + }, + formatterParams: formatterParams + }, tooltipOpt) + : null + }); + + if (axisModel.get('triggerEvent')) { + textEl.eventData = makeAxisEventDataBase(axisModel); + textEl.eventData.targetType = 'axisName'; + textEl.eventData.name = name; + } + + // FIXME + this._dumbGroup.add(textEl); + textEl.updateTransform(); + + this.group.add(textEl); + + textEl.decomposeTransform(); + } + + }; + + /** + * @inner + */ + function innerTextLayout(opt, textRotation, direction) { + var rotationDiff = remRadian(textRotation - opt.rotation); + var textAlign; + var verticalAlign; + + if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line. + verticalAlign = direction > 0 ? 'top' : 'bottom'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI)) { // Label is inverse parallel with axis line. + verticalAlign = direction > 0 ? 'bottom' : 'top'; + textAlign = 'center'; + } + else { + verticalAlign = 'middle'; + + if (rotationDiff > 0 && rotationDiff < PI) { + textAlign = direction > 0 ? 'right' : 'left'; + } + else { + textAlign = direction > 0 ? 'left' : 'right'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + verticalAlign: verticalAlign + }; + } + + /** + * @inner + */ + function endTextLayout(opt, textPosition, textRotate, extent) { + var rotationDiff = remRadian(textRotate - opt.rotation); + var textAlign; + var verticalAlign; + var inverse = extent[0] > extent[1]; + var onLeft = (textPosition === 'start' && !inverse) + || (textPosition !== 'start' && inverse); + + if (isRadianAroundZero(rotationDiff - PI / 2)) { + verticalAlign = onLeft ? 'bottom' : 'top'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI * 1.5)) { + verticalAlign = onLeft ? 'top' : 'bottom'; + textAlign = 'center'; + } + else { + verticalAlign = 'middle'; + if (rotationDiff < PI * 1.5 && rotationDiff > PI / 2) { + textAlign = onLeft ? 'left' : 'right'; + } + else { + textAlign = onLeft ? 'right' : 'left'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + verticalAlign: verticalAlign + }; + } + + /** + * @inner + */ + function isSilent(axisModel) { + var tooltipOpt = axisModel.get('tooltip'); + return axisModel.get('silent') + // Consider mouse cursor, add these restrictions. + || !( + axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show) + ); + } + + /** + * @static + */ + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick = function (axis, i, interval) { + var rawTick; + var scale = axis.scale; + return scale.type === 'ordinal' + && ( + typeof interval === 'function' + ? ( + rawTick = scale.getTicks()[i], + !interval(rawTick, scale.getLabel(rawTick)) + ) + : i % (interval + 1) + ); + }; + + /** + * @static + */ + var getInterval = AxisBuilder.getInterval = function (model, labelInterval) { + var interval = model.get('interval'); + if (interval == null || interval == 'auto') { + interval = labelInterval; + } + return interval; + }; + + module.exports = AxisBuilder; + + + +/***/ }, +/* 133 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + __webpack_require__(113); + + __webpack_require__(134); + __webpack_require__(135); + + var barLayoutGrid = __webpack_require__(137); + var echarts = __webpack_require__(1); + + echarts.registerLayout(zrUtil.curry(barLayoutGrid, 'bar')); + // Visual coding for legend + echarts.registerVisual(function (ecModel) { + ecModel.eachSeriesByType('bar', function (seriesModel) { + var data = seriesModel.getData(); + data.setVisual('legendSymbol', 'roundRect'); + }); + }); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 134 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var createListFromArray = __webpack_require__(101); + + module.exports = SeriesModel.extend({ + + type: 'series.bar', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + if (true) { + var coordSys = option.coordinateSystem; + if (coordSys !== 'cartesian2d') { + throw new Error('Bar only support cartesian2d coordinateSystem'); + } + } + return createListFromArray(option.data, this, ecModel); + }, + + getMarkerPosition: function (value) { + var coordSys = this.coordinateSystem; + if (coordSys) { + // PENDING if clamp ? + var pt = coordSys.dataToPoint(value, true); + var data = this.getData(); + var offset = data.getLayout('offset'); + var size = data.getLayout('size'); + var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1; + pt[offsetIndex] += offset + size / 2; + return pt; + } + return [NaN, NaN]; + }, + + brushSelector: 'rect', + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + // stack: null + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // 最小高度改为0 + barMinHeight: 0, + + // barMaxWidth: null, + // 默认自适应 + // barWidth: null, + // 柱间距离,默认为柱形宽度的30%,可设固定值 + // barGap: '30%', + // 类目间柱形距离,默认为类目间距的20%,可设固定值 + // barCategoryGap: '20%', + // label: { + // normal: { + // show: false + // } + // }, + itemStyle: { + normal: { + // color: '各异' + }, + emphasis: {} + } + } + }); + + +/***/ }, +/* 135 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + zrUtil.extend(__webpack_require__(12).prototype, __webpack_require__(136)); + + function fixLayoutWithLineWidth(layout, lineWidth) { + var signX = layout.width > 0 ? 1 : -1; + var signY = layout.height > 0 ? 1 : -1; + // In case width or height are too small. + lineWidth = Math.min(lineWidth, Math.abs(layout.width), Math.abs(layout.height)); + layout.x += signX * lineWidth / 2; + layout.y += signY * lineWidth / 2; + layout.width -= signX * lineWidth; + layout.height -= signY * lineWidth; + } + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'bar', + + render: function (seriesModel, ecModel, api) { + var coordinateSystemType = seriesModel.get('coordinateSystem'); + + if (coordinateSystemType === 'cartesian2d') { + this._renderOnCartesian(seriesModel, ecModel, api); + } + + return this.group; + }, + + _renderOnCartesian: function (seriesModel, ecModel, api) { + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + + var enableAnimation = seriesModel.get('animation'); + + var barBorderWidthQuery = ['itemStyle', 'normal', 'barBorderWidth']; + + function createRect(dataIndex, isUpdate) { + var layout = data.getItemLayout(dataIndex); + var lineWidth = data.getItemModel(dataIndex).get(barBorderWidthQuery) || 0; + fixLayoutWithLineWidth(layout, lineWidth); + + var rect = new graphic.Rect({ + shape: zrUtil.extend({}, layout) + }); + // Animation + if (enableAnimation) { + var rectShape = rect.shape; + var animateProperty = isHorizontal ? 'height' : 'width'; + var animateTarget = {}; + rectShape[animateProperty] = 0; + animateTarget[animateProperty] = layout[animateProperty]; + graphic[isUpdate? 'updateProps' : 'initProps'](rect, { + shape: animateTarget + }, seriesModel, dataIndex); + } + return rect; + } + data.diff(oldData) + .add(function (dataIndex) { + // 空数据 + if (!data.hasValue(dataIndex)) { + return; + } + + var rect = createRect(dataIndex); + + data.setItemGraphicEl(dataIndex, rect); + + group.add(rect); + + }) + .update(function (newIndex, oldIndex) { + var rect = oldData.getItemGraphicEl(oldIndex); + // 空数据 + if (!data.hasValue(newIndex)) { + group.remove(rect); + return; + } + if (!rect) { + rect = createRect(newIndex, true); + } + + var layout = data.getItemLayout(newIndex); + var lineWidth = data.getItemModel(newIndex).get(barBorderWidthQuery) || 0; + fixLayoutWithLineWidth(layout, lineWidth); + + graphic.updateProps(rect, { + shape: layout + }, seriesModel, newIndex); + + data.setItemGraphicEl(newIndex, rect); + + // Add back + group.add(rect); + }) + .remove(function (idx) { + var rect = oldData.getItemGraphicEl(idx); + if (rect) { + // Not show text when animating + rect.style.text = ''; + graphic.updateProps(rect, { + shape: { + width: 0 + } + }, seriesModel, idx, function () { + group.remove(rect); + }); + } + }) + .execute(); + + this._updateStyle(seriesModel, data, isHorizontal); + + this._data = data; + }, + + _updateStyle: function (seriesModel, data, isHorizontal) { + function setLabel(style, model, color, labelText, labelPositionOutside) { + graphic.setText(style, model, color); + style.text = labelText; + if (style.textPosition === 'outside') { + style.textPosition = labelPositionOutside; + } + } + + data.eachItemGraphicEl(function (rect, idx) { + var itemModel = data.getItemModel(idx); + var color = data.getItemVisual(idx, 'color'); + var opacity = data.getItemVisual(idx, 'opacity'); + var layout = data.getItemLayout(idx); + var itemStyleModel = itemModel.getModel('itemStyle.normal'); + + var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle(); + + rect.setShape('r', itemStyleModel.get('barBorderRadius') || 0); + + rect.useStyle(zrUtil.defaults( + { + fill: color, + opacity: opacity + }, + itemStyleModel.getBarItemStyle() + )); + + var labelPositionOutside = isHorizontal + ? (layout.height > 0 ? 'bottom' : 'top') + : (layout.width > 0 ? 'left' : 'right'); + + var labelModel = itemModel.getModel('label.normal'); + var hoverLabelModel = itemModel.getModel('label.emphasis'); + var rectStyle = rect.style; + if (labelModel.get('show')) { + setLabel( + rectStyle, labelModel, color, + zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal'), + seriesModel.getRawValue(idx) + ), + labelPositionOutside + ); + } + else { + rectStyle.text = ''; + } + if (hoverLabelModel.get('show')) { + setLabel( + hoverStyle, hoverLabelModel, color, + zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis'), + seriesModel.getRawValue(idx) + ), + labelPositionOutside + ); + } + else { + hoverStyle.text = ''; + } + graphic.setHoverStyle(rect, hoverStyle); + }); + }, + + remove: function (ecModel, api) { + var group = this.group; + if (ecModel.get('animation')) { + if (this._data) { + this._data.eachItemGraphicEl(function (el) { + // Not show text when animating + el.style.text = ''; + graphic.updateProps(el, { + shape: { + width: 0 + } + }, ecModel, el.dataIndex, function () { + group.remove(el); + }); + }); + } + } + else { + group.removeAll(); + } + } + }); + + +/***/ }, +/* 136 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var getBarItemStyle = __webpack_require__(15)( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + // Compatitable with 2 + ['stroke', 'barBorderColor'], + ['lineWidth', 'barBorderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getBarItemStyle: function (excludes) { + var style = getBarItemStyle.call(this, excludes); + if (this.getBorderLineDash) { + var lineDash = this.getBorderLineDash(); + lineDash && (style.lineDash = lineDash); + } + return style; + } + }; + + +/***/ }, +/* 137 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + + function getSeriesStackId(seriesModel) { + return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex; + } + + function getAxisKey(axis) { + return axis.dim + axis.index; + } + + function calBarWidthAndOffset(barSeries, api) { + // Columns info on each category axis. Key is cartesian name + var columnsMap = {}; + + zrUtil.each(barSeries, function (seriesModel, idx) { + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + + var baseAxis = cartesian.getBaseAxis(); + var axisExtent = baseAxis.getExtent(); + var bandWidth = baseAxis.type === 'category' + ? baseAxis.getBandWidth() + : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count()); + + var columnsOnAxis = columnsMap[getAxisKey(baseAxis)] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: '20%', + gap: '30%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[getAxisKey(baseAxis)] = columnsOnAxis; + + var stackId = getSeriesStackId(seriesModel); + + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + + var barWidth = parsePercent( + seriesModel.get('barWidth'), bandWidth + ); + var barMaxWidth = parsePercent( + seriesModel.get('barMaxWidth'), bandWidth + ); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + // TODO + if (barWidth && !stacks[stackId].width) { + barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); + stacks[stackId].width = barWidth; + columnsOnAxis.remainedWidth -= barWidth; + } + + barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); + (barGap != null) && (columnsOnAxis.gap = barGap); + (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap); + }); + + var result = {}; + + zrUtil.each(columnsMap, function (columnsOnAxis, coordSysName) { + + result[coordSysName] = {}; + + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGap = parsePercent(columnsOnAxis.categoryGap, bandWidth); + var barGapPercent = parsePercent(columnsOnAxis.gap, 1); + + var remainedWidth = columnsOnAxis.remainedWidth; + var autoWidthCount = columnsOnAxis.autoWidthCount; + var autoWidth = (remainedWidth - categoryGap) + / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + + // Find if any auto calculated bar exceeded maxBarWidth + zrUtil.each(stacks, function (column, stack) { + var maxWidth = column.maxWidth; + if (!column.width && maxWidth && maxWidth < autoWidth) { + maxWidth = Math.min(maxWidth, remainedWidth); + remainedWidth -= maxWidth; + column.width = maxWidth; + autoWidthCount--; + } + }); + + // Recalculate width again + autoWidth = (remainedWidth - categoryGap) + / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + + var widthSum = 0; + var lastColumn; + zrUtil.each(stacks, function (column, idx) { + if (!column.width) { + column.width = autoWidth; + } + lastColumn = column; + widthSum += column.width * (1 + barGapPercent); + }); + if (lastColumn) { + widthSum -= lastColumn.width * barGapPercent; + } + + var offset = -widthSum / 2; + zrUtil.each(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + offset: offset, + width: column.width + }; + + offset += column.width * (1 + barGapPercent); + }); + }); + + return result; + } + + /** + * @param {string} seriesType + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + function barLayoutGrid(seriesType, ecModel, api) { + + var barWidthAndOffset = calBarWidthAndOffset( + zrUtil.filter( + ecModel.getSeriesByType(seriesType), + function (seriesModel) { + return !ecModel.isSeriesFiltered(seriesModel) + && seriesModel.coordinateSystem + && seriesModel.coordinateSystem.type === 'cartesian2d'; + } + ) + ); + + var lastStackCoords = {}; + + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + + var stackId = getSeriesStackId(seriesModel); + var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; + var columnOffset = columnLayoutInfo.offset; + var columnWidth = columnLayoutInfo.width; + var valueAxis = cartesian.getOtherAxis(baseAxis); + + var barMinHeight = seriesModel.get('barMinHeight') || 0; + + var valueAxisStart = baseAxis.onZero + ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)) + : valueAxis.getGlobalExtent()[0]; + + var coords = cartesian.dataToPoints(data, true); + lastStackCoords[stackId] = lastStackCoords[stackId] || []; + + data.setLayout({ + offset: columnOffset, + size: columnWidth + }); + data.each(valueAxis.dim, function (value, idx) { + // 空数据 + if (isNaN(value)) { + return; + } + if (!lastStackCoords[stackId][idx]) { + lastStackCoords[stackId][idx] = { + // Positive stack + p: valueAxisStart, + // Negative stack + n: valueAxisStart + }; + } + var sign = value >= 0 ? 'p' : 'n'; + var coord = coords[idx]; + var lastCoord = lastStackCoords[stackId][idx][sign]; + var x, y, width, height; + if (valueAxis.isHorizontal()) { + x = lastCoord; + y = coord[1] + columnOffset; + width = coord[0] - lastCoord; + height = columnWidth; + + if (Math.abs(width) < barMinHeight) { + width = (width < 0 ? -1 : 1) * barMinHeight; + } + lastStackCoords[stackId][idx][sign] += width; + } + else { + x = coord[0] + columnOffset; + y = lastCoord; + width = columnWidth; + height = coord[1] - lastCoord; + if (Math.abs(height) < barMinHeight) { + // Include zero to has a positive bar + height = (height <= 0 ? -1 : 1) * barMinHeight; + } + lastStackCoords[stackId][idx][sign] += height; + } + + data.setItemLayout(idx, { + x: x, + y: y, + width: width, + height: height + }); + }, true); + + }, this); + } + + module.exports = barLayoutGrid; + + +/***/ }, +/* 138 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(139); + __webpack_require__(141); + + __webpack_require__(142)('pie', [{ + type: 'pieToggleSelect', + event: 'pieselectchanged', + method: 'toggleSelected' + }, { + type: 'pieSelect', + event: 'pieselected', + method: 'select' + }, { + type: 'pieUnSelect', + event: 'pieunselected', + method: 'unSelect' + }]); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'pie')); + + echarts.registerLayout(zrUtil.curry( + __webpack_require__(144), 'pie' + )); + + echarts.registerProcessor(zrUtil.curry(__webpack_require__(146), 'pie')); + + +/***/ }, +/* 139 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var completeDimensions = __webpack_require__(102); + + var dataSelectableMixin = __webpack_require__(140); + + var PieSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.pie', + + // Overwrite + init: function (option) { + PieSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + + this.updateSelectedMap(option.data); + + this._defaultLabelLine(option); + }, + + // Overwrite + mergeOption: function (newOption) { + PieSeries.superCall(this, 'mergeOption', newOption); + this.updateSelectedMap(this.option.data); + }, + + getInitialData: function (option, ecModel) { + var dimensions = completeDimensions(['value'], option.data); + var list = new List(dimensions, this); + list.initData(option.data); + return list; + }, + + // Overwrite + getDataParams: function (dataIndex) { + var data = this._data; + var params = PieSeries.superCall(this, 'getDataParams', dataIndex); + var sum = data.getSum('value'); + // FIXME toFixed? + // + // Percent is 0 if sum is 0 + params.percent = !sum ? 0 : +(data.get('value', dataIndex) / sum * 100).toFixed(2); + + params.$vars.push('percent'); + return params; + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + modelUtil.defaultEmphasis(option.labelLine, ['show']); + + var labelLineNormalOpt = option.labelLine.normal; + var labelLineEmphasisOpt = option.labelLine.emphasis; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.normal.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.label.emphasis.show; + }, + + defaultOption: { + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // 默认全局居中 + center: ['50%', '50%'], + radius: [0, '75%'], + // 默认顺时针 + clockwise: true, + startAngle: 90, + // 最小角度改为0 + minAngle: 0, + // 选中是扇区偏移量 + selectedOffset: 10, + + // If use strategy to avoid label overlapping + avoidLabelOverlap: true, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积) + // roseType: null, + + label: { + normal: { + // If rotate around circle + rotate: false, + show: true, + // 'outer', 'inside', 'center' + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 + }, + emphasis: {} + }, + // Enabled when label.normal.position is 'outer' + labelLine: { + normal: { + show: true, + // 引导线两段中的第一段长度 + length: 15, + // 引导线两段中的第二段长度 + length2: 15, + smooth: false, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + } + }, + itemStyle: { + normal: { + borderWidth: 1 + }, + emphasis: {} + }, + + animationEasing: 'cubicOut', + + data: [] + } + }); + + zrUtil.mixin(PieSeries, dataSelectableMixin); + + module.exports = PieSeries; + + +/***/ }, +/* 140 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Data selectable mixin for chart series. + * To eanble data select, option of series must have `selectedMode`. + * And each data item will use `selected` to toggle itself selected status + * + * @module echarts/chart/helper/DataSelectable + */ + + + var zrUtil = __webpack_require__(4); + + module.exports = { + + updateSelectedMap: function (targetList) { + this._selectTargetMap = zrUtil.reduce(targetList || [], function (targetMap, target) { + targetMap[target.name] = target; + return targetMap; + }, {}); + }, + /** + * @param {string} name + */ + // PENGING If selectedMode is null ? + select: function (name) { + var targetMap = this._selectTargetMap; + var target = targetMap[name]; + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + zrUtil.each(targetMap, function (target) { + target.selected = false; + }); + } + target && (target.selected = true); + }, + + /** + * @param {string} name + */ + unSelect: function (name) { + var target = this._selectTargetMap[name]; + // var selectedMode = this.get('selectedMode'); + // selectedMode !== 'single' && target && (target.selected = false); + target && (target.selected = false); + }, + + /** + * @param {string} name + */ + toggleSelected: function (name) { + var target = this._selectTargetMap[name]; + if (target != null) { + this[target.selected ? 'unSelect' : 'select'](name); + return target.selected; + } + }, + + /** + * @param {string} name + */ + isSelected: function (name) { + var target = this._selectTargetMap[name]; + return target && target.selected; + } + }; + + +/***/ }, +/* 141 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * @param {module:echarts/model/Series} seriesModel + * @param {boolean} hasAnimation + * @inner + */ + function updateDataSelected(uid, seriesModel, hasAnimation, api) { + var data = seriesModel.getData(); + var dataIndex = this.dataIndex; + var name = data.getName(dataIndex); + var selectedOffset = seriesModel.get('selectedOffset'); + + api.dispatchAction({ + type: 'pieToggleSelect', + from: uid, + name: name, + seriesId: seriesModel.id + }); + + data.each(function (idx) { + toggleItemSelected( + data.getItemGraphicEl(idx), + data.getItemLayout(idx), + seriesModel.isSelected(data.getName(idx)), + selectedOffset, + hasAnimation + ); + }); + } + + /** + * @param {module:zrender/graphic/Sector} el + * @param {Object} layout + * @param {boolean} isSelected + * @param {number} selectedOffset + * @param {boolean} hasAnimation + * @inner + */ + function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) { + var midAngle = (layout.startAngle + layout.endAngle) / 2; + + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + + var offset = isSelected ? selectedOffset : 0; + var position = [dx * offset, dy * offset]; + + hasAnimation + // animateTo will stop revious animation like update transition + ? el.animate() + .when(200, { + position: position + }) + .start('bounceOut') + : el.attr('position', position); + } + + /** + * Piece of pie including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ + function PiePiece(data, idx) { + + graphic.Group.call(this); + + var sector = new graphic.Sector({ + z2: 2 + }); + var polyline = new graphic.Polyline(); + var text = new graphic.Text(); + this.add(sector); + this.add(polyline); + this.add(text); + + this.updateData(data, idx, true); + + // Hover to change label and labelLine + function onEmphasis() { + polyline.ignore = polyline.hoverIgnore; + text.ignore = text.hoverIgnore; + } + function onNormal() { + polyline.ignore = polyline.normalIgnore; + text.ignore = text.normalIgnore; + } + this.on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('mouseover', onEmphasis) + .on('mouseout', onNormal); + } + + var piePieceProto = PiePiece.prototype; + + function getLabelStyle(data, idx, state, labelModel, labelPosition) { + var textStyleModel = labelModel.getModel('textStyle'); + var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; + return { + fill: textStyleModel.getTextColor() + || (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')), + opacity: data.getItemVisual(idx, 'opacity'), + textFont: textStyleModel.getFont(), + text: zrUtil.retrieve( + data.hostModel.getFormattedLabel(idx, state), data.getName(idx) + ) + }; + } + + piePieceProto.updateData = function (data, idx, firstCreate) { + + var sector = this.childAt(0); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var sectorShape = zrUtil.extend({}, layout); + sectorShape.label = null; + if (firstCreate) { + sector.setShape(sectorShape); + sector.shape.endAngle = layout.startAngle; + graphic.updateProps(sector, { + shape: { + endAngle: layout.endAngle + } + }, seriesModel, idx); + } + else { + graphic.updateProps(sector, { + shape: sectorShape + }, seriesModel, idx); + } + + // Update common style + var itemStyleModel = itemModel.getModel('itemStyle'); + var visualColor = data.getItemVisual(idx, 'color'); + + sector.useStyle( + zrUtil.defaults( + { + lineJoin: 'bevel', + fill: visualColor + }, + itemStyleModel.getModel('normal').getItemStyle() + ) + ); + sector.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); + + // Toggle selected + toggleItemSelected( + this, + data.getItemLayout(idx), + itemModel.get('selected'), + seriesModel.get('selectedOffset'), + seriesModel.get('animation') + ); + + function onEmphasis() { + // Sector may has animation of updating data. Force to move to the last frame + // Or it may stopped on the wrong shape + sector.stopAnimation(true); + sector.animateTo({ + shape: { + r: layout.r + 10 + } + }, 300, 'elasticOut'); + } + function onNormal() { + sector.stopAnimation(true); + sector.animateTo({ + shape: { + r: layout.r + } + }, 300, 'elasticOut'); + } + sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); + if (itemModel.get('hoverAnimation') && seriesModel.ifEnableAnimation()) { + sector + .on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } + + this._updateLabel(data, idx); + + graphic.setHoverStyle(this); + }; + + piePieceProto._updateLabel = function (data, idx) { + + var labelLine = this.childAt(1); + var labelText = this.childAt(2); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var labelLayout = layout.label; + var visualColor = data.getItemVisual(idx, 'color'); + + graphic.updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || [ + [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y] + ] + } + }, seriesModel, idx); + + graphic.updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + style: { + textVerticalAlign: labelLayout.verticalAlign, + textAlign: labelLayout.textAlign, + textFont: labelLayout.font + }, + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineHoverModel = itemModel.getModel('labelLine.emphasis'); + var labelPosition = labelModel.get('position') || labelHoverModel.get('position'); + + labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel, labelPosition)); + + labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); + labelText.hoverIgnore = !labelHoverModel.get('show'); + + labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); + labelLine.hoverIgnore = !labelLineHoverModel.get('show'); + + // Default use item visual color + labelLine.setStyle({ + stroke: visualColor, + opacity: data.getItemVisual(idx, 'opacity') + }); + labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); + + labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel, labelPosition); + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); + + var smooth = labelLineModel.get('smooth'); + if (smooth && smooth === true) { + smooth = 0.4; + } + labelLine.setShape({ + smooth: smooth + }); + }; + + zrUtil.inherits(PiePiece, graphic.Group); + + + // Pie view + var Pie = __webpack_require__(42).extend({ + + type: 'pie', + + init: function () { + var sectorGroup = new graphic.Group(); + this._sectorGroup = sectorGroup; + }, + + render: function (seriesModel, ecModel, api, payload) { + if (payload && (payload.from === this.uid)) { + return; + } + + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + + var hasAnimation = ecModel.get('animation'); + var isFirstRender = !oldData; + + var onSectorClick = zrUtil.curry( + updateDataSelected, this.uid, seriesModel, hasAnimation, api + ); + + var selectedMode = seriesModel.get('selectedMode'); + + data.diff(oldData) + .add(function (idx) { + var piePiece = new PiePiece(data, idx); + if (isFirstRender) { + piePiece.eachChild(function (child) { + child.stopAnimation(true); + }); + } + + selectedMode && piePiece.on('click', onSectorClick); + + data.setItemGraphicEl(idx, piePiece); + + group.add(piePiece); + }) + .update(function (newIdx, oldIdx) { + var piePiece = oldData.getItemGraphicEl(oldIdx); + + piePiece.updateData(data, newIdx); + + piePiece.off('click'); + selectedMode && piePiece.on('click', onSectorClick); + group.add(piePiece); + data.setItemGraphicEl(newIdx, piePiece); + }) + .remove(function (idx) { + var piePiece = oldData.getItemGraphicEl(idx); + group.remove(piePiece); + }) + .execute(); + + if (hasAnimation && isFirstRender && data.count() > 0) { + var shape = data.getItemLayout(0); + var r = Math.max(api.getWidth(), api.getHeight()) / 2; + + var removeClipPath = zrUtil.bind(group.removeClipPath, group); + group.setClipPath(this._createClipPath( + shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel + )); + } + + this._data = data; + }, + + _createClipPath: function ( + cx, cy, r, startAngle, clockwise, cb, seriesModel + ) { + var clipPath = new graphic.Sector({ + shape: { + cx: cx, + cy: cy, + r0: 0, + r: r, + startAngle: startAngle, + endAngle: startAngle, + clockwise: clockwise + } + }); + + graphic.initProps(clipPath, { + shape: { + endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2 + } + }, seriesModel, cb); + + return clipPath; + } + }); + + module.exports = Pie; + + +/***/ }, +/* 142 */ +/***/ function(module, exports, __webpack_require__) { + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + module.exports = function (seriesType, actionInfos) { + zrUtil.each(actionInfos, function (actionInfo) { + actionInfo.update = 'updateView'; + /** + * @payload + * @property {string} seriesName + * @property {string} name + */ + echarts.registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + ecModel.eachComponent( + {mainType: 'series', subType: seriesType, query: payload}, + function (seriesModel) { + if (seriesModel[actionInfo.method]) { + seriesModel[actionInfo.method](payload.name); + } + var data = seriesModel.getData(); + // Create selected map + data.each(function (idx) { + var name = data.getName(idx); + selected[name] = seriesModel.isSelected(name) || false; + }); + } + ); + return { + name: payload.name, + selected: selected + }; + }); + }); + }; + + +/***/ }, +/* 143 */ +/***/ function(module, exports) { + + // Pick color from palette for each data item + + + module.exports = function (seriesType, ecModel) { + // Pie and funnel may use diferrent scope + var paletteScope = {}; + ecModel.eachRawSeriesByType(seriesType, function (seriesModel) { + var dataAll = seriesModel.getRawData(); + var idxMap = {}; + if (!ecModel.isSeriesFiltered(seriesModel)) { + var data = seriesModel.getData(); + data.each(function (idx) { + var rawIdx = data.getRawIndex(idx); + idxMap[rawIdx] = idx; + }); + dataAll.each(function (rawIdx) { + // FIXME Performance + var itemModel = dataAll.getItemModel(rawIdx); + var filteredIdx = idxMap[rawIdx]; + + // If series.itemStyle.normal.color is a function. itemVisual may be encoded + var singleDataColor = filteredIdx != null + && data.getItemVisual(filteredIdx, 'color', true); + + if (!singleDataColor) { + var color = itemModel.get('itemStyle.normal.color') + || seriesModel.getColorFromPalette(dataAll.getName(rawIdx), paletteScope); + // Legend may use the visual info in data before processed + dataAll.setItemVisual(rawIdx, 'color', color); + + // Data is not filtered + if (filteredIdx != null) { + data.setItemVisual(filteredIdx, 'color', color); + } + } + else { + // Set data all color for legend + dataAll.setItemVisual(rawIdx, 'color', singleDataColor); + } + }); + } + }); + }; + + +/***/ }, +/* 144 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO minAngle + + + + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var labelLayout = __webpack_require__(145); + var zrUtil = __webpack_require__(4); + + var PI2 = Math.PI * 2; + var RADIAN = Math.PI / 180; + + module.exports = function (seriesType, ecModel, api, payload) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + + if (!zrUtil.isArray(radius)) { + radius = [0, radius]; + } + if (!zrUtil.isArray(center)) { + center = [center, center]; + } + + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent(center[0], width); + var cy = parsePercent(center[1], height); + var r0 = parsePercent(radius[0], size / 2); + var r = parsePercent(radius[1], size / 2); + + var data = seriesModel.getData(); + + var startAngle = -seriesModel.get('startAngle') * RADIAN; + + var minAngle = seriesModel.get('minAngle') * RADIAN; + + var sum = data.getSum('value'); + // Sum may be 0 + var unitRadian = Math.PI / (sum || data.count()) * 2; + + var clockwise = seriesModel.get('clockwise'); + + var roseType = seriesModel.get('roseType'); + + // [0...max] + var extent = data.getDataExtent('value'); + extent[0] = 0; + + // In the case some sector angle is smaller than minAngle + var restAngle = PI2; + var valueSumLargerThanMinAngle = 0; + + var currentAngle = startAngle; + + var dir = clockwise ? 1 : -1; + data.each('value', function (value, idx) { + var angle; + // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? + if (roseType !== 'area') { + angle = sum === 0 ? unitRadian : (value * unitRadian); + } + else { + angle = PI2 / (data.count() || 1); + } + + if (angle < minAngle) { + angle = minAngle; + restAngle -= minAngle; + } + else { + valueSumLargerThanMinAngle += value; + } + + var endAngle = currentAngle + dir * angle; + data.setItemLayout(idx, { + angle: angle, + startAngle: currentAngle, + endAngle: endAngle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: r0, + r: roseType + ? numberUtil.linearMap(value, extent, [r0, r]) + : r + }); + + currentAngle = endAngle; + }, true); + + // Some sector is constrained by minAngle + // Rest sectors needs recalculate angle + if (restAngle < PI2) { + // Average the angle if rest angle is not enough after all angles is + // Constrained by minAngle + if (restAngle <= 1e-3) { + var angle = PI2 / data.count(); + data.each(function (idx) { + var layout = data.getItemLayout(idx); + layout.startAngle = startAngle + dir * idx * angle; + layout.endAngle = startAngle + dir * (idx + 1) * angle; + }); + } + else { + unitRadian = restAngle / valueSumLargerThanMinAngle; + currentAngle = startAngle; + data.each('value', function (value, idx) { + var layout = data.getItemLayout(idx); + var angle = layout.angle === minAngle + ? minAngle : value * unitRadian; + layout.startAngle = currentAngle; + layout.endAngle = currentAngle + dir * angle; + currentAngle += angle; + }); + } + } + + labelLayout(seriesModel, r, width, height); + }); + }; + + +/***/ }, +/* 145 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // FIXME emphasis label position is not same with normal label position + + + var textContain = __webpack_require__(8); + + function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { + list.sort(function (a, b) { + return a.y - b.y; + }); + + // 压 + function shiftDown(start, end, delta, dir) { + for (var j = start; j < end; j++) { + list[j].y += delta; + if (j > start + && j + 1 < end + && list[j + 1].y > list[j].y + list[j].height + ) { + shiftUp(j, delta / 2); + return; + } + } + + shiftUp(end - 1, delta / 2); + } + + // 弹 + function shiftUp(end, delta) { + for (var j = end; j >= 0; j--) { + list[j].y -= delta; + if (j > 0 + && list[j].y > list[j - 1].y + list[j - 1].height + ) { + break; + } + } + } + + function changeX(list, isDownList, cx, cy, r, dir) { + var lastDeltaX = dir > 0 + ? isDownList // 右侧 + ? Number.MAX_VALUE // 下 + : 0 // 上 + : isDownList // 左侧 + ? Number.MAX_VALUE // 下 + : 0; // 上 + + for (var i = 0, l = list.length; i < l; i++) { + // Not change x for center label + if (list[i].position === 'center') { + continue; + } + var deltaY = Math.abs(list[i].y - cy); + var length = list[i].len; + var length2 = list[i].len2; + var deltaX = (deltaY < r + length) + ? Math.sqrt( + (r + length + length2) * (r + length + length2) + - deltaY * deltaY + ) + : Math.abs(list[i].x - cx); + if (isDownList && deltaX >= lastDeltaX) { + // 右下,左下 + deltaX = lastDeltaX - 10; + } + if (!isDownList && deltaX <= lastDeltaX) { + // 右上,左上 + deltaX = lastDeltaX + 10; + } + + list[i].x = cx + deltaX * dir; + lastDeltaX = deltaX; + } + } + + var lastY = 0; + var delta; + var len = list.length; + var upList = []; + var downList = []; + for (var i = 0; i < len; i++) { + delta = list[i].y - lastY; + if (delta < 0) { + shiftDown(i, len, -delta, dir); + } + lastY = list[i].y + list[i].height; + } + if (viewHeight - lastY < 0) { + shiftUp(len - 1, lastY - viewHeight); + } + for (var i = 0; i < len; i++) { + if (list[i].y >= cy) { + downList.push(list[i]); + } + else { + upList.push(list[i]); + } + } + changeX(upList, false, cx, cy, r, dir); + changeX(downList, true, cx, cy, r, dir); + } + + function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) { + var leftList = []; + var rightList = []; + for (var i = 0; i < labelLayoutList.length; i++) { + if (labelLayoutList[i].x < cx) { + leftList.push(labelLayoutList[i]); + } + else { + rightList.push(labelLayoutList[i]); + } + } + + adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight); + adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight); + + for (var i = 0; i < labelLayoutList.length; i++) { + var linePoints = labelLayoutList[i].linePoints; + if (linePoints) { + var dist = linePoints[1][0] - linePoints[2][0]; + if (labelLayoutList[i].x < cx) { + linePoints[2][0] = labelLayoutList[i].x + 3; + } + else { + linePoints[2][0] = labelLayoutList[i].x - 3; + } + linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y; + linePoints[1][0] = linePoints[2][0] + dist; + } + } + } + + module.exports = function (seriesModel, r, viewWidth, viewHeight) { + var data = seriesModel.getData(); + var labelLayoutList = []; + var cx; + var cy; + var hasLabelRotate = false; + + data.each(function (idx) { + var layout = data.getItemLayout(idx); + + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + // Use position in normal or emphasis + var labelPosition = labelModel.get('position') || itemModel.get('label.emphasis.position'); + + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineLen = labelLineModel.get('length'); + var labelLineLen2 = labelLineModel.get('length2'); + + var midAngle = (layout.startAngle + layout.endAngle) / 2; + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + + var textX; + var textY; + var linePoints; + var textAlign; + + cx = layout.cx; + cy = layout.cy; + + var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; + if (labelPosition === 'center') { + textX = layout.cx; + textY = layout.cy; + textAlign = 'center'; + } + else { + var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx; + var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy; + + textX = x1 + dx * 3; + textY = y1 + dy * 3; + + if (!isLabelInside) { + // For roseType + var x2 = x1 + dx * (labelLineLen + r - layout.r); + var y2 = y1 + dy * (labelLineLen + r - layout.r); + var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2); + var y3 = y2; + + textX = x3 + (dx < 0 ? -5 : 5); + textY = y3; + linePoints = [[x1, y1], [x2, y2], [x3, y3]]; + } + + textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right'); + } + var font = labelModel.getModel('textStyle').getFont(); + + var labelRotate = labelModel.get('rotate') + ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0; + var text = seriesModel.getFormattedLabel(idx, 'normal') + || data.getName(idx); + var textRect = textContain.getBoundingRect( + text, font, textAlign, 'top' + ); + hasLabelRotate = !!labelRotate; + layout.label = { + x: textX, + y: textY, + position: labelPosition, + height: textRect.height, + len: labelLineLen, + len2: labelLineLen2, + linePoints: linePoints, + textAlign: textAlign, + verticalAlign: 'middle', + font: font, + rotation: labelRotate + }; + + // Not layout the inside label + if (!isLabelInside) { + labelLayoutList.push(layout.label); + } + }); + if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { + avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight); + } + }; + + +/***/ }, +/* 146 */ +/***/ function(module, exports) { + + + module.exports = function (seriesType, ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + ecModel.eachSeriesByType(seriesType, function (series) { + var data = series.getData(); + data.filterSelf(function (idx) { + var name = data.getName(idx); + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(name)) { + return false; + } + } + return true; + }, this); + }, this); + }; + + +/***/ }, +/* 147 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(148); + __webpack_require__(149); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'scatter', 'circle', null + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'scatter' + )); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 148 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.scatter', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + var list = createListFromArray(option.data, this, ecModel); + return list; + }, + + brushSelector: 'point', + + defaultOption: { + coordinateSystem: 'cartesian2d', + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Polar coordinate system + // polarIndex: 0, + + // Geo coordinate system + // geoIndex: 0, + + // symbol: null, // 图形类型 + symbolSize: 10, // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2 + // symbolRotate: null, // 图形旋转控制 + + large: false, + // Available when large is true + largeThreshold: 2000, + + // label: { + // normal: { + // show: false + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为 + // 'inside'|'left'|'right'|'top'|'bottom' + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + // } + // }, + itemStyle: { + normal: { + opacity: 0.8 + // color: 各异 + } + } + } + + }); + + +/***/ }, +/* 149 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var LargeSymbolDraw = __webpack_require__(150); + + __webpack_require__(1).extendChartView({ + + type: 'scatter', + + init: function () { + this._normalSymbolDraw = new SymbolDraw(); + this._largeSymbolDraw = new LargeSymbolDraw(); + }, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var largeSymbolDraw = this._largeSymbolDraw; + var normalSymbolDraw = this._normalSymbolDraw; + var group = this.group; + + var symbolDraw = seriesModel.get('large') && data.count() > seriesModel.get('largeThreshold') + ? largeSymbolDraw : normalSymbolDraw; + + this._symbolDraw = symbolDraw; + symbolDraw.updateData(data); + group.add(symbolDraw.group); + + group.remove( + symbolDraw === largeSymbolDraw + ? normalSymbolDraw.group : largeSymbolDraw.group + ); + }, + + updateLayout: function (seriesModel) { + this._symbolDraw.updateLayout(seriesModel); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(api, true); + } + }); + + +/***/ }, +/* 150 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Batch by color + + + + var graphic = __webpack_require__(43); + var symbolUtil = __webpack_require__(106); + + var LargeSymbolPath = graphic.extendShape({ + + shape: { + points: null, + sizes: null + }, + + symbolProxy: null, + + buildPath: function (path, shape) { + var points = shape.points; + var sizes = shape.sizes; + + var symbolProxy = this.symbolProxy; + var symbolProxyShape = symbolProxy.shape; + for (var i = 0; i < points.length; i++) { + var pt = points[i]; + var size = sizes[i]; + if (size[0] < 4) { + // Optimize for small symbol + path.rect( + pt[0] - size[0] / 2, pt[1] - size[1] / 2, + size[0], size[1] + ); + } + else { + symbolProxyShape.x = pt[0] - size[0] / 2; + symbolProxyShape.y = pt[1] - size[1] / 2; + symbolProxyShape.width = size[0]; + symbolProxyShape.height = size[1]; + + symbolProxy.buildPath(path, symbolProxyShape, true); + } + } + }, + + findDataIndex: function (x, y) { + var shape = this.shape; + var points = shape.points; + var sizes = shape.sizes; + + // Not consider transform + // Treat each element as a rect + // top down traverse + for (var i = points.length - 1; i >= 0; i--) { + var pt = points[i]; + var size = sizes[i]; + var x0 = pt[0] - size[0] / 2; + var y0 = pt[1] - size[1] / 2; + if (x >= x0 && y >= y0 && x <= x0 + size[0] && y <= y0 + size[1]) { + // i is dataIndex + return i; + } + } + + return -1; + } + }); + + function LargeSymbolDraw() { + this.group = new graphic.Group(); + + this._symbolEl = new LargeSymbolPath({ + // rectHover: true, + // cursor: 'default' + }); + } + + var largeSymbolProto = LargeSymbolDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + largeSymbolProto.updateData = function (data) { + this.group.removeAll(); + + var symbolEl = this._symbolEl; + + var seriesModel = data.hostModel; + + symbolEl.setShape({ + points: data.mapArray(data.getItemLayout), + sizes: data.mapArray( + function (idx) { + var size = data.getItemVisual(idx, 'symbolSize'); + if (!(size instanceof Array)) { + size = [size, size]; + } + return size; + } + ) + }); + + // Create symbolProxy to build path for each data + symbolEl.symbolProxy = symbolUtil.createSymbol( + data.getVisual('symbol'), 0, 0, 0, 0 + ); + // Use symbolProxy setColor method + symbolEl.setColor = symbolEl.symbolProxy.setColor; + + symbolEl.useStyle( + seriesModel.getModel('itemStyle.normal').getItemStyle(['color']) + ); + + var visualColor = data.getVisual('color'); + if (visualColor) { + symbolEl.setColor(visualColor); + } + + // Enable tooltip + // PENDING May have performance issue when path is extremely large + symbolEl.seriesIndex = seriesModel.seriesIndex; + symbolEl.on('mousemove', function (e) { + symbolEl.dataIndex = null; + var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY); + if (dataIndex > 0) { + // Provide dataIndex for tooltip + symbolEl.dataIndex = dataIndex; + } + }); + + // Add back + this.group.add(symbolEl); + }; + + largeSymbolProto.updateLayout = function (seriesModel) { + var data = seriesModel.getData(); + this._symbolEl.setShape({ + points: data.mapArray(data.getItemLayout) + }); + }; + + largeSymbolProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LargeSymbolDraw; + + +/***/ }, +/* 151 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + // Must use radar component + __webpack_require__(152); + + __webpack_require__(157); + __webpack_require__(158); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'radar')); + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'radar', 'circle', null + )); + echarts.registerLayout(__webpack_require__(159)); + + echarts.registerProcessor( + zrUtil.curry(__webpack_require__(146), 'radar') + ); + + echarts.registerPreprocessor(__webpack_require__(160)); + + +/***/ }, +/* 152 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(153); + __webpack_require__(155); + + __webpack_require__(156); + + +/***/ }, +/* 153 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO clockwise + + + var zrUtil = __webpack_require__(4); + var IndicatorAxis = __webpack_require__(154); + var IntervalScale = __webpack_require__(117); + var numberUtil = __webpack_require__(7); + var axisHelper = __webpack_require__(114); + + function Radar(radarModel, ecModel, api) { + + this._model = radarModel; + /** + * Radar dimensions + * @type {Array.} + */ + this.dimensions = []; + + this._indicatorAxes = zrUtil.map(radarModel.getIndicatorModels(), function (indicatorModel, idx) { + var dim = 'indicator_' + idx; + var indicatorAxis = new IndicatorAxis(dim, new IntervalScale()); + indicatorAxis.name = indicatorModel.get('name'); + // Inject model and axis + indicatorAxis.model = indicatorModel; + indicatorModel.axis = indicatorAxis; + this.dimensions.push(dim); + return indicatorAxis; + }, this); + + this.resize(radarModel, api); + + /** + * @type {number} + * @readOnly + */ + this.cx; + /** + * @type {number} + * @readOnly + */ + this.cy; + /** + * @type {number} + * @readOnly + */ + this.r; + /** + * @type {number} + * @readOnly + */ + this.startAngle; + } + + Radar.prototype.getIndicatorAxes = function () { + return this._indicatorAxes; + }; + + Radar.prototype.dataToPoint = function (value, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + + return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex); + }; + + Radar.prototype.coordToPoint = function (coord, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + var angle = indicatorAxis.angle; + var x = this.cx + coord * Math.cos(angle); + var y = this.cy - coord * Math.sin(angle); + return [x, y]; + }; + + Radar.prototype.pointToData = function (pt) { + var dx = pt[0] - this.cx; + var dy = pt[1] - this.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + + var radian = Math.atan2(-dy, dx); + + // Find the closest angle + // FIXME index can calculated directly + var minRadianDiff = Infinity; + var closestAxis; + var closestAxisIdx = -1; + for (var i = 0; i < this._indicatorAxes.length; i++) { + var indicatorAxis = this._indicatorAxes[i]; + var diff = Math.abs(radian - indicatorAxis.angle); + if (diff < minRadianDiff) { + closestAxis = indicatorAxis; + closestAxisIdx = i; + minRadianDiff = diff; + } + } + + return [closestAxisIdx, +(closestAxis && closestAxis.coodToData(radius))]; + }; + + Radar.prototype.resize = function (radarModel, api) { + var center = radarModel.get('center'); + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + var viewSize = Math.min(viewWidth, viewHeight) / 2; + this.cx = numberUtil.parsePercent(center[0], viewWidth); + this.cy = numberUtil.parsePercent(center[1], viewHeight); + + this.startAngle = radarModel.get('startAngle') * Math.PI / 180; + + this.r = numberUtil.parsePercent(radarModel.get('radius'), viewSize); + + zrUtil.each(this._indicatorAxes, function (indicatorAxis, idx) { + indicatorAxis.setExtent(0, this.r); + var angle = (this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length); + // Normalize to [-PI, PI] + angle = Math.atan2(Math.sin(angle), Math.cos(angle)); + indicatorAxis.angle = angle; + }, this); + }; + + Radar.prototype.update = function (ecModel, api) { + var indicatorAxes = this._indicatorAxes; + var radarModel = this._model; + zrUtil.each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.setExtent(Infinity, -Infinity); + }); + ecModel.eachSeriesByType('radar', function (radarSeries, idx) { + if (radarSeries.get('coordinateSystem') !== 'radar' + || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel + ) { + return; + } + var data = radarSeries.getData(); + zrUtil.each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.unionExtent(data.getDataExtent(indicatorAxis.dim)); + }); + }, this); + + var splitNumber = radarModel.get('splitNumber'); + + function increaseInterval(interval) { + var exp10 = Math.pow(10, Math.floor(Math.log(interval) / Math.LN10)); + // Increase interval + var f = interval / exp10; + if (f === 2) { + f = 5; + } + else { // f is 2 or 5 + f *= 2; + } + return f * exp10; + } + // Force all the axis fixing the maxSplitNumber. + zrUtil.each(indicatorAxes, function (indicatorAxis, idx) { + var rawExtent = axisHelper.getScaleExtent(indicatorAxis, indicatorAxis.model); + axisHelper.niceScaleExtent(indicatorAxis, indicatorAxis.model); + + var axisModel = indicatorAxis.model; + var scale = indicatorAxis.scale; + var fixedMin = axisModel.get('min'); + var fixedMax = axisModel.get('max'); + var interval = scale.getInterval(); + + if (fixedMin != null && fixedMax != null) { + // User set min, max, divide to get new interval + // FIXME precision + scale.setInterval( + (fixedMax - fixedMin) / splitNumber + ); + } + else if (fixedMin != null) { + var max; + // User set min, expand extent on the other side + do { + max = fixedMin + interval * splitNumber; + scale.setExtent(+fixedMin, max); + // Interval must been set after extent + // FIXME + scale.setInterval(interval); + + interval = increaseInterval(interval); + } while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])); + } + else if (fixedMax != null) { + var min; + // User set min, expand extent on the other side + do { + min = fixedMax - interval * splitNumber; + scale.setExtent(min, +fixedMax); + scale.setInterval(interval); + interval = increaseInterval(interval); + } while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])); + } + else { + var nicedSplitNumber = scale.getTicks().length - 1; + if (nicedSplitNumber > splitNumber) { + interval = increaseInterval(interval); + } + // PENDING + var center = Math.round((rawExtent[0] + rawExtent[1]) / 2 / interval) * interval; + var halfSplitNumber = Math.round(splitNumber / 2); + scale.setExtent( + numberUtil.round(center - halfSplitNumber * interval), + numberUtil.round(center + (splitNumber - halfSplitNumber) * interval) + ); + scale.setInterval(interval); + } + }); + }; + + /** + * Radar dimensions is based on the data + * @type {Array} + */ + Radar.dimensions = []; + + Radar.create = function (ecModel, api) { + var radarList = []; + ecModel.eachComponent('radar', function (radarModel) { + var radar = new Radar(radarModel, ecModel, api); + radarList.push(radar); + radarModel.coordinateSystem = radar; + }); + ecModel.eachSeriesByType('radar', function (radarSeries) { + if (radarSeries.get('coordinateSystem') === 'radar') { + // Inject coordinate system + radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0]; + } + }); + return radarList; + }; + + __webpack_require__(26).register('radar', Radar); + module.exports = Radar; + + +/***/ }, +/* 154 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function IndicatorAxis(dim, scale, radiusExtent) { + Axis.call(this, dim, scale, radiusExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'value'; + + this.angle = 0; + + /** + * Indicator name + * @type {string} + */ + this.name = ''; + /** + * @type {module:echarts/model/Model} + */ + this.model; + } + + zrUtil.inherits(IndicatorAxis, Axis); + + module.exports = IndicatorAxis; + + +/***/ }, +/* 155 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var axisDefault = __webpack_require__(128); + var valueAxisDefault = axisDefault.valueAxis; + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var axisModelCommonMixin = __webpack_require__(129); + + function defaultsShow(opt, show) { + return zrUtil.defaults({ + show: show + }, opt); + } + + var RadarModel = __webpack_require__(1).extendComponentModel({ + + type: 'radar', + + optionUpdated: function () { + var boundaryGap = this.get('boundaryGap'); + var splitNumber = this.get('splitNumber'); + var scale = this.get('scale'); + var axisLine = this.get('axisLine'); + var axisTick = this.get('axisTick'); + var axisLabel = this.get('axisLabel'); + var nameTextStyle = this.get('name.textStyle'); + var showName = this.get('name.show'); + var nameFormatter = this.get('name.formatter'); + var nameGap = this.get('nameGap'); + var indicatorModels = zrUtil.map(this.get('indicator') || [], function (indicatorOpt) { + // PENDING + if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) { + indicatorOpt.min = 0; + } + else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) { + indicatorOpt.max = 0; + } + // Use same configuration + indicatorOpt = zrUtil.merge(zrUtil.clone(indicatorOpt), { + boundaryGap: boundaryGap, + splitNumber: splitNumber, + scale: scale, + axisLine: axisLine, + axisTick: axisTick, + axisLabel: axisLabel, + // Competitable with 2 and use text + name: indicatorOpt.text, + nameLocation: 'end', + nameGap: nameGap, + // min: 0, + nameTextStyle: nameTextStyle + }, false); + if (!showName) { + indicatorOpt.name = ''; + } + if (typeof nameFormatter === 'string') { + indicatorOpt.name = nameFormatter.replace('{value}', indicatorOpt.name); + } + else if (typeof nameFormatter === 'function') { + indicatorOpt.name = nameFormatter( + indicatorOpt.name, indicatorOpt + ); + } + return zrUtil.extend( + new Model(indicatorOpt, null, this.ecModel), + axisModelCommonMixin + ); + }, this); + this.getIndicatorModels = function () { + return indicatorModels; + }; + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + center: ['50%', '50%'], + + radius: '75%', + + startAngle: 90, + + name: { + show: true + // formatter: null + // textStyle: {} + }, + + boundaryGap: [0, 0], + + splitNumber: 5, + + nameGap: 15, + + scale: false, + + // Polygon or circle + shape: 'polygon', + + axisLine: zrUtil.merge( + { + lineStyle: { + color: '#bbb' + } + }, + valueAxisDefault.axisLine + ), + axisLabel: defaultsShow(valueAxisDefault.axisLabel, false), + axisTick: defaultsShow(valueAxisDefault.axisTick, false), + splitLine: defaultsShow(valueAxisDefault.splitLine, true), + splitArea: defaultsShow(valueAxisDefault.splitArea, true), + + // {text, min, max} + indicator: [] + } + }); + + module.exports = RadarModel; + + +/***/ }, +/* 156 */ +/***/ function(module, exports, __webpack_require__) { + + + + var AxisBuilder = __webpack_require__(132); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'radar', + + render: function (radarModel, ecModel, api) { + var group = this.group; + group.removeAll(); + + this._buildAxes(radarModel); + this._buildSplitLineAndArea(radarModel); + }, + + _buildAxes: function (radarModel) { + var radar = radarModel.coordinateSystem; + var indicatorAxes = radar.getIndicatorAxes(); + var axisBuilders = zrUtil.map(indicatorAxes, function (indicatorAxis) { + var axisBuilder = new AxisBuilder(indicatorAxis.model, { + position: [radar.cx, radar.cy], + rotation: indicatorAxis.angle, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1 + }); + return axisBuilder; + }); + + zrUtil.each(axisBuilders, function (axisBuilder) { + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + }, this); + }, + + _buildSplitLineAndArea: function (radarModel) { + var radar = radarModel.coordinateSystem; + var splitNumber = radarModel.get('splitNumber'); + var indicatorAxes = radar.getIndicatorAxes(); + if (!indicatorAxes.length) { + return; + } + var shape = radarModel.get('shape'); + var splitLineModel = radarModel.getModel('splitLine'); + var splitAreaModel = radarModel.getModel('splitArea'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + + var showSplitLine = splitLineModel.get('show'); + var showSplitArea = splitAreaModel.get('show'); + var splitLineColors = lineStyleModel.get('color'); + var splitAreaColors = areaStyleModel.get('color'); + + splitLineColors = zrUtil.isArray(splitLineColors) ? splitLineColors : [splitLineColors]; + splitAreaColors = zrUtil.isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors]; + + var splitLines = []; + var splitAreas = []; + + function getColorIndex(areaOrLine, areaOrLineColorList, idx) { + var colorIndex = idx % areaOrLineColorList.length; + areaOrLine[colorIndex] = areaOrLine[colorIndex] || []; + return colorIndex; + } + + if (shape === 'circle') { + var ticksRadius = indicatorAxes[0].getTicksCoords(); + var cx = radar.cx; + var cy = radar.cy; + for (var i = 0; i < ticksRadius.length; i++) { + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColors, i); + splitLines[colorIndex].push(new graphic.Circle({ + shape: { + cx: cx, + cy: cy, + r: ticksRadius[i] + } + })); + } + if (showSplitArea && i < ticksRadius.length - 1) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i); + splitAreas[colorIndex].push(new graphic.Ring({ + shape: { + cx: cx, + cy: cy, + r0: ticksRadius[i], + r: ticksRadius[i + 1] + } + })); + } + } + } + // Polyyon + else { + var axesTicksPoints = zrUtil.map(indicatorAxes, function (indicatorAxis, idx) { + var ticksCoords = indicatorAxis.getTicksCoords(); + return zrUtil.map(ticksCoords, function (tickCoord) { + return radar.coordToPoint(tickCoord, idx); + }); + }); + + var prevPoints = []; + for (var i = 0; i <= splitNumber; i++) { + var points = []; + for (var j = 0; j < indicatorAxes.length; j++) { + points.push(axesTicksPoints[j][i]); + } + // Close + if (points[0]) { + points.push(points[0].slice()); + } + else { + if (true) { + console.error('Can\'t draw value axis ' + i); + continue; + } + } + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColors, i); + splitLines[colorIndex].push(new graphic.Polyline({ + shape: { + points: points + } + })); + } + if (showSplitArea && prevPoints) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i - 1); + splitAreas[colorIndex].push(new graphic.Polygon({ + shape: { + points: points.concat(prevPoints) + } + })); + } + prevPoints = points.slice().reverse(); + } + } + + var lineStyle = lineStyleModel.getLineStyle(); + var areaStyle = areaStyleModel.getAreaStyle(); + // Add splitArea before splitLine + zrUtil.each(splitAreas, function (splitAreas, idx) { + this.group.add(graphic.mergePath( + splitAreas, { + style: zrUtil.defaults({ + stroke: 'none', + fill: splitAreaColors[idx % splitAreaColors.length] + }, areaStyle), + silent: true + } + )); + }, this); + + zrUtil.each(splitLines, function (splitLines, idx) { + this.group.add(graphic.mergePath( + splitLines, { + style: zrUtil.defaults({ + fill: 'none', + stroke: splitLineColors[idx % splitLineColors.length] + }, lineStyle), + silent: true + } + )); + }, this); + + } + }); + + +/***/ }, +/* 157 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var zrUtil = __webpack_require__(4); + + var RadarSeries = SeriesModel.extend({ + + type: 'series.radar', + + dependencies: ['radar'], + + + // Overwrite + init: function (option) { + RadarSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + }, + + getInitialData: function (option, ecModel) { + var data = option.data || []; + var dimensions = completeDimensions( + [], data, [], 'indicator_' + ); + var list = new List(dimensions, this); + list.initData(data); + return list; + }, + + formatTooltip: function (dataIndex) { + var value = this.getRawValue(dataIndex); + var coordSys = this.coordinateSystem; + var indicatorAxes = coordSys.getIndicatorAxes(); + return (this._data.getName(dataIndex) == '' ? this.name : this._data.getName(dataIndex)) + '
' + + zrUtil.map(indicatorAxes, function (axis, idx) { + return axis.name + ' : ' + value[idx]; + }).join('
'); + }, + + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: 'radar', + legendHoverLink: true, + radarIndex: 0, + lineStyle: { + normal: { + width: 2, + type: 'solid' + } + }, + label: { + normal: { + position: 'top' + } + }, + // areaStyle: { + // }, + // itemStyle: {} + symbol: 'emptyCircle', + symbolSize: 4 + // symbolRotate: null + } + }); + + module.exports = RadarSeries; + + +/***/ }, +/* 158 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + + function normalizeSymbolSize(symbolSize) { + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + module.exports = __webpack_require__(1).extendChartView({ + type: 'radar', + + render: function (seriesModel, ecModel, api) { + var polar = seriesModel.coordinateSystem; + var group = this.group; + + var data = seriesModel.getData(); + var oldData = this._data; + + function createSymbol(data, idx) { + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var color = data.getItemVisual(idx, 'color'); + if (symbolType === 'none') { + return; + } + var symbolPath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + symbolPath.attr({ + style: { + strokeNoScale: true + }, + z2: 100, + scale: normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')) + }); + return symbolPath; + } + + function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) { + // Simply rerender all + symbolGroup.removeAll(); + for (var i = 0; i < newPoints.length - 1; i++) { + var symbolPath = createSymbol(data, idx); + if (symbolPath) { + symbolPath.__dimIdx = i; + if (oldPoints[i]) { + symbolPath.attr('position', oldPoints[i]); + graphic[isInit ? 'initProps' : 'updateProps']( + symbolPath, { + position: newPoints[i] + }, seriesModel, idx + ); + } + else { + symbolPath.attr('position', newPoints[i]); + } + symbolGroup.add(symbolPath); + } + } + } + + function getInitialPoints(points) { + return zrUtil.map(points, function (pt) { + return [polar.cx, polar.cy]; + }); + } + data.diff(oldData) + .add(function (idx) { + var points = data.getItemLayout(idx); + if (!points) { + return; + } + var polygon = new graphic.Polygon(); + var polyline = new graphic.Polyline(); + var target = { + shape: { + points: points + } + }; + polygon.shape.points = getInitialPoints(points); + polyline.shape.points = getInitialPoints(points); + graphic.initProps(polygon, target, seriesModel, idx); + graphic.initProps(polyline, target, seriesModel, idx); + + var itemGroup = new graphic.Group(); + var symbolGroup = new graphic.Group(); + itemGroup.add(polyline); + itemGroup.add(polygon); + itemGroup.add(symbolGroup); + + updateSymbols( + polyline.shape.points, points, symbolGroup, data, idx, true + ); + + data.setItemGraphicEl(idx, itemGroup); + }) + .update(function (newIdx, oldIdx) { + var itemGroup = oldData.getItemGraphicEl(oldIdx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + var target = { + shape: { + points: data.getItemLayout(newIdx) + } + }; + if (!target.shape.points) { + return; + } + updateSymbols( + polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false + ); + + graphic.updateProps(polyline, target, seriesModel); + graphic.updateProps(polygon, target, seriesModel); + + data.setItemGraphicEl(newIdx, itemGroup); + }) + .remove(function (idx) { + group.remove(oldData.getItemGraphicEl(idx)); + }) + .execute(); + + data.eachItemGraphicEl(function (itemGroup, idx) { + var itemModel = data.getItemModel(idx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + var color = data.getItemVisual(idx, 'color'); + + group.add(itemGroup); + + polyline.useStyle( + zrUtil.defaults( + itemModel.getModel('lineStyle.normal').getLineStyle(), + { + fill: 'none', + stroke: color + } + ) + ); + polyline.hoverStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + + var areaStyleModel = itemModel.getModel('areaStyle.normal'); + var hoverAreaStyleModel = itemModel.getModel('areaStyle.emphasis'); + var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); + var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty(); + + hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore; + polygon.ignore = polygonIgnore; + + polygon.useStyle( + zrUtil.defaults( + areaStyleModel.getAreaStyle(), + { + fill: color, + opacity: 0.7 + } + ) + ); + polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle(); + + var itemStyle = itemModel.getModel('itemStyle.normal').getItemStyle(['color']); + var itemHoverStyle = itemModel.getModel('itemStyle.emphasis').getItemStyle(); + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + symbolGroup.eachChild(function (symbolPath) { + symbolPath.setStyle(itemStyle); + symbolPath.hoverStyle = zrUtil.clone(itemHoverStyle); + + var defaultText = data.get(data.dimensions[symbolPath.__dimIdx], idx); + graphic.setText(symbolPath.style, labelModel, color); + symbolPath.setStyle({ + text: labelModel.get('show') ? zrUtil.retrieve( + seriesModel.getFormattedLabel( + idx, 'normal', null, symbolPath.__dimIdx + ), + defaultText + ) : '' + }); + + graphic.setText(symbolPath.hoverStyle, labelHoverModel, color); + symbolPath.hoverStyle.text = labelHoverModel.get('show') ? zrUtil.retrieve( + seriesModel.getFormattedLabel( + idx, 'emphasis', null, symbolPath.__dimIdx + ), + defaultText + ) : ''; + }); + + function onEmphasis() { + polygon.attr('ignore', hoverPolygonIgnore); + } + + function onNormal() { + polygon.attr('ignore', polygonIgnore); + } + + itemGroup.off('mouseover').off('mouseout').off('normal').off('emphasis'); + itemGroup.on('emphasis', onEmphasis) + .on('mouseover', onEmphasis) + .on('normal', onNormal) + .on('mouseout', onNormal); + + graphic.setHoverStyle(itemGroup); + }); + + this._data = data; + }, + + remove: function () { + this.group.removeAll(); + this._data = null; + } + }); + + +/***/ }, +/* 159 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('radar', function (seriesModel) { + var data = seriesModel.getData(); + var points = []; + var coordSys = seriesModel.coordinateSystem; + if (!coordSys) { + return; + } + + function pointsConverter(val, idx) { + points[idx] = points[idx] || []; + points[idx][i] = coordSys.dataToPoint(val, i); + } + for (var i = 0; i < coordSys.getIndicatorAxes().length; i++) { + var dim = data.dimensions[i]; + data.each(dim, pointsConverter); + } + + data.each(function (idx) { + // Close polygon + points[idx][0] && points[idx].push(points[idx][0].slice()); + data.setItemLayout(idx, points[idx]); + }); + }); + }; + + +/***/ }, +/* 160 */ +/***/ function(module, exports, __webpack_require__) { + + // Backward compat for radar chart in 2 + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + var polarOptArr = option.polar; + if (polarOptArr) { + if (!zrUtil.isArray(polarOptArr)) { + polarOptArr = [polarOptArr]; + } + var polarNotRadar = []; + zrUtil.each(polarOptArr, function (polarOpt, idx) { + if (polarOpt.indicator) { + if (polarOpt.type && !polarOpt.shape) { + polarOpt.shape = polarOpt.type; + } + option.radar = option.radar || []; + if (!zrUtil.isArray(option.radar)) { + option.radar = [option.radar]; + } + option.radar.push(polarOpt); + } + else { + polarNotRadar.push(polarOpt); + } + }); + option.polar = polarNotRadar; + } + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt.type === 'radar' && seriesOpt.polarIndex) { + seriesOpt.radarIndex = seriesOpt.polarIndex; + } + }); + }; + + +/***/ }, +/* 161 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var PRIORITY = echarts.PRIORITY; + + __webpack_require__(162); + + __webpack_require__(172); + + __webpack_require__(176); + + __webpack_require__(163); + + echarts.registerLayout(__webpack_require__(178)); + + echarts.registerVisual(__webpack_require__(179)); + + echarts.registerProcessor(PRIORITY.PROCESSOR.STATISTIC, __webpack_require__(180)); + + echarts.registerPreprocessor(__webpack_require__(181)); + + __webpack_require__(142)('map', [{ + type: 'mapToggleSelect', + event: 'mapselectchanged', + method: 'toggleSelected' + }, { + type: 'mapSelect', + event: 'mapselected', + method: 'select' + }, { + type: 'mapUnSelect', + event: 'mapunselected', + method: 'unSelect' + }]); + + +/***/ }, +/* 162 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var SeriesModel = __webpack_require__(28); + var zrUtil = __webpack_require__(4); + var completeDimensions = __webpack_require__(102); + + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var dataSelectableMixin = __webpack_require__(140); + + var geoCreator = __webpack_require__(163); + + var MapSeries = SeriesModel.extend({ + + type: 'series.map', + + layoutMode: 'box', + + /** + * Only first map series of same mapType will drawMap + * @type {boolean} + */ + needsDrawMap: false, + + /** + * Group of all map series with same mapType + * @type {boolean} + */ + seriesGroup: [], + + init: function (option) { + + option = this._fillOption(option, option.map); + this.option = option; + + MapSeries.superApply(this, 'init', arguments); + + this.updateSelectedMap(option.data); + }, + + getInitialData: function (option) { + var dimensions = completeDimensions(['value'], option.data || []); + + var list = new List(dimensions, this); + + list.initData(option.data); + + return list; + }, + + mergeOption: function (newOption) { + if (newOption.data) { + newOption = this._fillOption(newOption, this.option.map); + } + + MapSeries.superCall(this, 'mergeOption', newOption); + + this.updateSelectedMap(this.option.data); + }, + + _fillOption: function (option, mapName) { + // Shallow clone + option = zrUtil.extend({}, option); + + option.data = geoCreator.getFilledRegions(option.data, mapName); + + return option; + }, + + getRawValue: function (dataIndex) { + // Use value stored in data instead because it is calculated from multiple series + // FIXME Provide all value of multiple series ? + return this._data.get('value', dataIndex); + }, + + /** + * Get model of region + * @param {string} name + * @return {module:echarts/model/Model} + */ + getRegionModel: function (regionName) { + var data = this.getData(); + return data.getItemModel(data.indexOfName(regionName)); + }, + + /** + * Map tooltip formatter + * + * @param {number} dataIndex + */ + formatTooltip: function (dataIndex) { + // FIXME orignalData and data is a bit confusing + var data = this.getData(); + var formattedValue = addCommas(this.getRawValue(dataIndex)); + var name = data.getName(dataIndex); + + var seriesGroup = this.seriesGroup; + var seriesNames = []; + for (var i = 0; i < seriesGroup.length; i++) { + var otherIndex = seriesGroup[i].originalData.indexOfName(name); + if (!isNaN(seriesGroup[i].originalData.get('value', otherIndex))) { + seriesNames.push( + encodeHTML(seriesGroup[i].name) + ); + } + } + + return seriesNames.join(', ') + '
' + + name + ' : ' + formattedValue; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 2, + coordinateSystem: 'geo', + // 各省的 map 暂时都用中文 + map: 'china', + + // 'center' | 'left' | 'right' | 'x%' | {number} + left: 'center', + // 'center' | 'top' | 'bottom' | 'x%' | {number} + top: 'center', + // right + // bottom + // width: + // height + + // Aspect is width / height. Inited to be geoJson bbox aspect + // This parameter is used for scale this aspect + aspectScale: 0.75, + + ///// Layout with center and size + // If you wan't to put map in a fixed size box with right aspect ratio + // This two properties may more conveninet + // layoutCenter: [50%, 50%] + // layoutSize: 100 + + + // 数值合并方式,默认加和,可选为: + // 'sum' | 'average' | 'max' | 'min' + // mapValueCalculation: 'sum', + // 地图数值计算结果小数精度 + // mapValuePrecision: 0, + + + // 显示图例颜色标识(系列标识的小圆点),图例开启时有效 + showLegendSymbol: true, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + dataRangeHoverLink: true, + // 是否开启缩放及漫游模式 + // roam: false, + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + label: { + normal: { + show: false, + textStyle: { + color: '#000' + } + }, + emphasis: { + show: true, + textStyle: { + color: 'rgb(100,0,0)' + } + } + }, + // scaleLimit: null, + itemStyle: { + normal: { + // color: 各异, + borderWidth: 0.5, + borderColor: '#444', + areaColor: '#eee' + }, + // 也是选中样式 + emphasis: { + areaColor: 'rgba(255,215,0,0.8)' + } + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + } + }); + + zrUtil.mixin(MapSeries, dataSelectableMixin); + + module.exports = MapSeries; + + +/***/ }, +/* 163 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Geo = __webpack_require__(164); + + var layout = __webpack_require__(21); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var mapDataStores = {}; + + /** + * Resize method bound to the geo + * @param {module:echarts/coord/geo/GeoModel|module:echarts/chart/map/MapModel} geoModel + * @param {module:echarts/ExtensionAPI} api + */ + function resizeGeo (geoModel, api) { + var rect = this.getBoundingRect(); + + var boxLayoutOption; + + var center = geoModel.get('layoutCenter'); + var size = geoModel.get('layoutSize'); + + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + + var aspectScale = geoModel.get('aspectScale') || 0.75; + var aspect = rect.width / rect.height * aspectScale; + + var useCenterAndSize = false; + if (center && size) { + center = [ + numberUtil.parsePercent(center[0], viewWidth), + numberUtil.parsePercent(center[1], viewHeight) + ]; + size = numberUtil.parsePercent(size, Math.min(viewWidth, viewHeight)); + + if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) { + useCenterAndSize = true; + } + else { + if (true) { + console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.'); + } + } + } + + var viewRect; + if (useCenterAndSize) { + var viewRect = {}; + if (aspect > 1) { + // Width is same with size + viewRect.width = size; + viewRect.height = size / aspect; + } + else { + viewRect.height = size; + viewRect.width = size * aspect; + } + viewRect.y = center[1] - viewRect.height / 2; + viewRect.x = center[0] - viewRect.width / 2; + } + else { + // Use left/top/width/height + boxLayoutOption = geoModel.getBoxLayoutParams(); + + // 0.75 rate + boxLayoutOption.aspect = aspect; + + viewRect = layout.getLayoutRect(boxLayoutOption, { + width: viewWidth, + height: viewHeight + }); + } + + this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height); + + this.setCenter(geoModel.get('center')); + this.setZoom(geoModel.get('zoom')); + } + + /** + * @param {module:echarts/coord/Geo} geo + * @param {module:echarts/model/Model} model + * @inner + */ + function setGeoCoords(geo, model) { + zrUtil.each(model.get('geoCoord'), function (geoCoord, name) { + geo.addGeoCoord(name, geoCoord); + }); + } + + if (true) { + var mapNotExistsError = function (name) { + console.error('Map ' + name + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html'); + }; + } + + var geoCreator = { + + // For deciding which dimensions to use when creating list data + dimensions: Geo.prototype.dimensions, + + create: function (ecModel, api) { + var geoList = []; + + // FIXME Create each time may be slow + ecModel.eachComponent('geo', function (geoModel, idx) { + var name = geoModel.get('map'); + var mapData = mapDataStores[name]; + if (true) { + if (!mapData) { + mapNotExistsError(name); + } + } + var geo = new Geo( + name + idx, name, + mapData && mapData.geoJson, mapData && mapData.specialAreas, + geoModel.get('nameMap') + ); + geo.zoomLimit = geoModel.get('scaleLimit'); + geoList.push(geo); + + setGeoCoords(geo, geoModel); + + geoModel.coordinateSystem = geo; + geo.model = geoModel; + + // Inject resize method + geo.resize = resizeGeo; + + geo.resize(geoModel, api); + }); + + ecModel.eachSeries(function (seriesModel) { + var coordSys = seriesModel.get('coordinateSystem'); + if (coordSys === 'geo') { + var geoIndex = seriesModel.get('geoIndex') || 0; + seriesModel.coordinateSystem = geoList[geoIndex]; + } + }); + + // If has map series + var mapModelGroupBySeries = {}; + + ecModel.eachSeriesByType('map', function (seriesModel) { + var mapType = seriesModel.get('map'); + + mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || []; + + mapModelGroupBySeries[mapType].push(seriesModel); + }); + + zrUtil.each(mapModelGroupBySeries, function (mapSeries, mapType) { + var mapData = mapDataStores[mapType]; + if (true) { + if (!mapData) { + mapNotExistsError(mapSeries[0].get('map')); + } + } + + var nameMapList = zrUtil.map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('nameMap'); + }); + var geo = new Geo( + mapType, mapType, + mapData && mapData.geoJson, mapData && mapData.specialAreas, + zrUtil.mergeAll(nameMapList) + ); + geo.zoomLimit = zrUtil.retrieve.apply(null, zrUtil.map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('scaleLimit'); + })); + geoList.push(geo); + + // Inject resize method + geo.resize = resizeGeo; + + geo.resize(mapSeries[0], api); + + zrUtil.each(mapSeries, function (singleMapSeries) { + singleMapSeries.coordinateSystem = geo; + + setGeoCoords(geo, singleMapSeries); + }); + }); + + return geoList; + }, + + /** + * @param {string} mapName + * @param {Object|string} geoJson + * @param {Object} [specialAreas] + * + * @example + * $.get('USA.json', function (geoJson) { + * echarts.registerMap('USA', geoJson); + * // Or + * echarts.registerMap('USA', { + * geoJson: geoJson, + * specialAreas: {} + * }) + * }); + */ + registerMap: function (mapName, geoJson, specialAreas) { + if (geoJson.geoJson && !geoJson.features) { + specialAreas = geoJson.specialAreas; + geoJson = geoJson.geoJson; + } + if (typeof geoJson === 'string') { + geoJson = (typeof JSON !== 'undefined' && JSON.parse) + ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))(); + } + mapDataStores[mapName] = { + geoJson: geoJson, + specialAreas: specialAreas + }; + }, + + /** + * @param {string} mapName + * @return {Object} + */ + getMap: function (mapName) { + return mapDataStores[mapName]; + }, + + /** + * Fill given regions array + * @param {Array.} originRegionArr + * @param {string} mapName + * @return {Array} + */ + getFilledRegions: function (originRegionArr, mapName) { + // Not use the original + var regionsArr = (originRegionArr || []).slice(); + + var map = geoCreator.getMap(mapName); + var geoJson = map && map.geoJson; + if (!geoJson) { + if (true) { + mapNotExistsError(mapName); + } + return originRegionArr; + } + + var dataNameMap = {}; + var features = geoJson.features; + for (var i = 0; i < regionsArr.length; i++) { + dataNameMap[regionsArr[i].name] = regionsArr[i]; + } + + for (var i = 0; i < features.length; i++) { + var name = features[i].properties.name; + if (!dataNameMap[name]) { + regionsArr.push({ + name: name + }); + } + } + return regionsArr; + } + }; + + // Inject methods into echarts + var echarts = __webpack_require__(1); + + echarts.registerMap = geoCreator.registerMap; + + echarts.getMap = geoCreator.getMap; + + // TODO + echarts.loadMap = function () {}; + + echarts.registerCoordinateSystem('geo', geoCreator); + + module.exports = geoCreator; + + +/***/ }, +/* 164 */ +/***/ function(module, exports, __webpack_require__) { + + + + var parseGeoJson = __webpack_require__(165); + + var zrUtil = __webpack_require__(4); + + var BoundingRect = __webpack_require__(9); + + var View = __webpack_require__(168); + + + // Geo fix functions + var geoFixFuncs = [ + __webpack_require__(169), + __webpack_require__(170), + __webpack_require__(171) + ]; + + /** + * [Geo description] + * @param {string} name Geo name + * @param {string} map Map type + * @param {Object} geoJson + * @param {Object} [specialAreas] + * Specify the positioned areas by left, top, width, height + * @param {Object.} [nameMap] + * Specify name alias + */ + function Geo(name, map, geoJson, specialAreas, nameMap) { + + View.call(this, name); + + /** + * Map type + * @type {string} + */ + this.map = map; + + this._nameCoordMap = {}; + + this.loadGeoJson(geoJson, specialAreas, nameMap); + } + + Geo.prototype = { + + constructor: Geo, + + type: 'geo', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['lng', 'lat'], + + /** + * If contain given lng,lat coord + * @param {Array.} + * @readOnly + */ + containCoord: function (coord) { + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + if (regions[i].contain(coord)) { + return true; + } + } + return false; + }, + /** + * @param {Object} geoJson + * @param {Object} [specialAreas] + * Specify the positioned areas by left, top, width, height + * @param {Object.} [nameMap] + * Specify name alias + */ + loadGeoJson: function (geoJson, specialAreas, nameMap) { + // https://jsperf.com/try-catch-performance-overhead + try { + this.regions = geoJson ? parseGeoJson(geoJson) : []; + } + catch (e) { + throw 'Invalid geoJson format\n' + e; + } + specialAreas = specialAreas || {}; + nameMap = nameMap || {}; + var regions = this.regions; + var regionsMap = {}; + for (var i = 0; i < regions.length; i++) { + var regionName = regions[i].name; + // Try use the alias in nameMap + regionName = nameMap[regionName] || regionName; + regions[i].name = regionName; + + regionsMap[regionName] = regions[i]; + // Add geoJson + this.addGeoCoord(regionName, regions[i].center); + + // Some area like Alaska in USA map needs to be tansformed + // to look better + var specialArea = specialAreas[regionName]; + if (specialArea) { + regions[i].transformTo( + specialArea.left, specialArea.top, specialArea.width, specialArea.height + ); + } + } + + this._regionsMap = regionsMap; + + this._rect = null; + + zrUtil.each(geoFixFuncs, function (fixFunc) { + fixFunc(this); + }, this); + }, + + // Overwrite + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + + rect = rect.clone(); + // Longitute is inverted + rect.y = -rect.y - rect.height; + + var viewTransform = this._viewTransform; + + viewTransform.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + viewTransform.decomposeTransform(); + + var scale = viewTransform.scale; + scale[1] = -scale[1]; + + viewTransform.updateTransform(); + + this._updateTransform(); + }, + + /** + * @param {string} name + * @return {module:echarts/coord/geo/Region} + */ + getRegion: function (name) { + return this._regionsMap[name]; + }, + + getRegionByCoord: function (coord) { + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + if (regions[i].contain(coord)) { + return regions[i]; + } + } + }, + + /** + * Add geoCoord for indexing by name + * @param {string} name + * @param {Array.} geoCoord + */ + addGeoCoord: function (name, geoCoord) { + this._nameCoordMap[name] = geoCoord; + }, + + /** + * Get geoCoord by name + * @param {string} name + * @return {Array.} + */ + getGeoCoord: function (name) { + return this._nameCoordMap[name]; + }, + + // Overwrite + getBoundingRect: function () { + if (this._rect) { + return this._rect; + } + var rect; + + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + var regionRect = regions[i].getBoundingRect(); + rect = rect || regionRect.clone(); + rect.union(regionRect); + } + // FIXME Always return new ? + return (this._rect = rect || new BoundingRect(0, 0, 0, 0)); + }, + + /** + * Convert series data to a list of points + * @param {module:echarts/data/List} data + * @param {boolean} stack + * @return {Array} + * Return list of points. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data) { + var item = []; + return data.mapArray(['lng', 'lat'], function (lon, lat) { + item[0] = lon; + item[1] = lat; + return this.dataToPoint(item); + }, this); + }, + + // Overwrite + /** + * @param {string|Array.} data + * @return {Array.} + */ + dataToPoint: function (data) { + if (typeof data === 'string') { + // Map area name to geoCoord + data = this.getGeoCoord(data); + } + if (data) { + return View.prototype.dataToPoint.call(this, data); + } + } + }; + + zrUtil.mixin(Geo, View); + + module.exports = Geo; + + +/***/ }, +/* 165 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parse and decode geo json + * @module echarts/coord/geo/parseGeoJson + */ + + + var zrUtil = __webpack_require__(4); + + var Region = __webpack_require__(166); + + function decode(json) { + if (!json.UTF8Encoding) { + return json; + } + var features = json.features; + + for (var f = 0; f < features.length; f++) { + var feature = features[f]; + var geometry = feature.geometry; + var coordinates = geometry.coordinates; + var encodeOffsets = geometry.encodeOffsets; + + for (var c = 0; c < coordinates.length; c++) { + var coordinate = coordinates[c]; + + if (geometry.type === 'Polygon') { + coordinates[c] = decodePolygon( + coordinate, + encodeOffsets[c] + ); + } + else if (geometry.type === 'MultiPolygon') { + for (var c2 = 0; c2 < coordinate.length; c2++) { + var polygon = coordinate[c2]; + coordinate[c2] = decodePolygon( + polygon, + encodeOffsets[c][c2] + ); + } + } + } + } + // Has been decoded + json.UTF8Encoding = false; + return json; + } + + function decodePolygon(coordinate, encodeOffsets) { + var result = []; + var prevX = encodeOffsets[0]; + var prevY = encodeOffsets[1]; + + for (var i = 0; i < coordinate.length; i += 2) { + var x = coordinate.charCodeAt(i) - 64; + var y = coordinate.charCodeAt(i + 1) - 64; + // ZigZag decoding + x = (x >> 1) ^ (-(x & 1)); + y = (y >> 1) ^ (-(y & 1)); + // Delta deocding + x += prevX; + y += prevY; + + prevX = x; + prevY = y; + // Dequantize + result.push([x / 1024, y / 1024]); + } + + return result; + } + + /** + * @inner + */ + function flattern2D(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + for (var k = 0; k < array[i].length; k++) { + ret.push(array[i][k]); + } + } + return ret; + } + + /** + * @alias module:echarts/coord/geo/parseGeoJson + * @param {Object} geoJson + * @return {module:zrender/container/Group} + */ + module.exports = function (geoJson) { + + decode(geoJson); + + return zrUtil.map(zrUtil.filter(geoJson.features, function (featureObj) { + // Output of mapshaper may have geometry null + return featureObj.geometry && featureObj.properties; + }), function (featureObj) { + var properties = featureObj.properties; + var geometry = featureObj.geometry; + + var coordinates = geometry.coordinates; + + if (geometry.type === 'MultiPolygon') { + coordinates = flattern2D(coordinates); + } + + return new Region( + properties.name, + coordinates, + properties.cp + ); + }); + }; + + +/***/ }, +/* 166 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/coord/geo/Region + */ + + + var polygonContain = __webpack_require__(167); + + var BoundingRect = __webpack_require__(9); + + var bbox = __webpack_require__(51); + var vec2 = __webpack_require__(10); + + /** + * @param {string} name + * @param {Array} contours + * @param {Array.} cp + */ + function Region(name, contours, cp) { + + /** + * @type {string} + * @readOnly + */ + this.name = name; + + /** + * @type {Array.} + * @readOnly + */ + this.contours = contours; + + if (!cp) { + var rect = this.getBoundingRect(); + cp = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } + else { + cp = [cp[0], cp[1]]; + } + /** + * @type {Array.} + */ + this.center = cp; + } + + Region.prototype = { + + constructor: Region, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function () { + var rect = this._rect; + if (rect) { + return rect; + } + + var MAX_NUMBER = Number.MAX_VALUE; + var min = [MAX_NUMBER, MAX_NUMBER]; + var max = [-MAX_NUMBER, -MAX_NUMBER]; + var min2 = []; + var max2 = []; + var contours = this.contours; + for (var i = 0; i < contours.length; i++) { + bbox.fromPoints(contours[i], min2, max2); + vec2.min(min, min, min2); + vec2.max(max, max, max2); + } + // No data + if (i === 0) { + min[0] = min[1] = max[0] = max[1] = 0; + } + + return (this._rect = new BoundingRect( + min[0], min[1], max[0] - min[0], max[1] - min[1] + )); + }, + + /** + * @param {} coord + * @return {boolean} + */ + contain: function (coord) { + var rect = this.getBoundingRect(); + var contours = this.contours; + if (rect.contain(coord[0], coord[1])) { + for (var i = 0, len = contours.length; i < len; i++) { + if (polygonContain.contain(contours[i], coord[0], coord[1])) { + return true; + } + } + } + return false; + }, + + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + var aspect = rect.width / rect.height; + if (!width) { + width = aspect * height; + } + else if (!height) { + height = width / aspect ; + } + var target = new BoundingRect(x, y, width, height); + var transform = rect.calculateTransform(target); + var contours = this.contours; + for (var i = 0; i < contours.length; i++) { + for (var p = 0; p < contours[i].length; p++) { + vec2.applyTransform(contours[i][p], contours[i][p], transform); + } + } + rect = this._rect; + rect.copy(target); + // Update center + this.center = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } + }; + + module.exports = Region; + + +/***/ }, +/* 167 */ +/***/ function(module, exports, __webpack_require__) { + + + + var windingLine = __webpack_require__(58); + + var EPSILON = 1e-8; + + function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON; + } + + function contain(points, x, y) { + var w = 0; + var p = points[0]; + + if (!p) { + return false; + } + + for (var i = 1; i < points.length; i++) { + var p2 = points[i]; + w += windingLine(p[0], p[1], p2[0], p2[1], x, y); + p = p2; + } + + // Close polygon + var p0 = points[0]; + if (!isAroundEqual(p[0], p0[0]) || !isAroundEqual(p[1], p0[1])) { + w += windingLine(p[0], p[1], p0[0], p0[1], x, y); + } + + return w !== 0; + } + + + module.exports = { + contain: contain + }; + + +/***/ }, +/* 168 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Simple view coordinate system + * Mapping given x, y to transformd view x, y + */ + + + var vector = __webpack_require__(10); + var matrix = __webpack_require__(11); + + var Transformable = __webpack_require__(34); + var zrUtil = __webpack_require__(4); + + var BoundingRect = __webpack_require__(9); + + var v2ApplyTransform = vector.applyTransform; + + // Dummy transform node + function TransformDummy() { + Transformable.call(this); + } + zrUtil.mixin(TransformDummy, Transformable); + + function View(name) { + /** + * @type {string} + */ + this.name = name; + + /** + * @type {Object} + */ + this.zoomLimit; + + Transformable.call(this); + + this._roamTransform = new TransformDummy(); + + this._viewTransform = new TransformDummy(); + + this._center; + this._zoom; + } + + View.prototype = { + + constructor: View, + + type: 'view', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['x', 'y'], + + /** + * Set bounding rect + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + + // PENDING to getRect + setBoundingRect: function (x, y, width, height) { + this._rect = new BoundingRect(x, y, width, height); + return this._rect; + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + // PENDING to getRect + getBoundingRect: function () { + return this._rect; + }, + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + setViewRect: function (x, y, width, height) { + width = width; + height = height; + this.transformTo(x, y, width, height); + this._viewRect = new BoundingRect(x, y, width, height); + }, + + /** + * Transformed to particular position and size + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + var viewTransform = this._viewTransform; + + viewTransform.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + viewTransform.decomposeTransform(); + + this._updateTransform(); + }, + + /** + * Set center of view + * @param {Array.} [centerCoord] + */ + setCenter: function (centerCoord) { + if (!centerCoord) { + return; + } + this._center = centerCoord; + + this._updateCenterAndZoom(); + }, + + /** + * @param {number} zoom + */ + setZoom: function (zoom) { + zoom = zoom || 1; + + var zoomLimit = this.zoomLimit; + if (zoomLimit) { + if (zoomLimit.max != null) { + zoom = Math.min(zoomLimit.max, zoom); + } + if (zoomLimit.min != null) { + zoom = Math.max(zoomLimit.min, zoom); + } + } + this._zoom = zoom; + + this._updateCenterAndZoom(); + }, + + /** + * Get default center without roam + */ + getDefaultCenter: function () { + // Rect before any transform + var rawRect = this.getBoundingRect(); + var cx = rawRect.x + rawRect.width / 2; + var cy = rawRect.y + rawRect.height / 2; + + return [cx, cy]; + }, + + getCenter: function () { + return this._center || this.getDefaultCenter(); + }, + + getZoom: function () { + return this._zoom || 1; + }, + + /** + * @return {Array.} data + * @return {Array.} + */ + dataToPoint: function (data) { + var transform = this.transform; + return transform + ? v2ApplyTransform([], data, transform) + : [data[0], data[1]]; + }, + + /** + * Convert a (x, y) point to (lon, lat) data + * @param {Array.} point + * @return {Array.} + */ + pointToData: function (point) { + var invTransform = this.invTransform; + return invTransform + ? v2ApplyTransform([], point, invTransform) + : [point[0], point[1]]; + } + + /** + * @return {number} + */ + // getScalarScale: function () { + // // Use determinant square root of transform to mutiply scalar + // var m = this.transform; + // var det = Math.sqrt(Math.abs(m[0] * m[3] - m[2] * m[1])); + // return det; + // } + }; + + zrUtil.mixin(View, Transformable); + + module.exports = View; + + +/***/ }, +/* 169 */ +/***/ function(module, exports, __webpack_require__) { + + // Fix for 南海诸岛 + + + var Region = __webpack_require__(166); + + var geoCoord = [126, 25]; + + var points = [ + [[0,3.5],[7,11.2],[15,11.9],[30,7],[42,0.7],[52,0.7], + [56,7.7],[59,0.7],[64,0.7],[64,0],[5,0],[0,3.5]], + [[13,16.1],[19,14.7],[16,21.7],[11,23.1],[13,16.1]], + [[12,32.2],[14,38.5],[15,38.5],[13,32.2],[12,32.2]], + [[16,47.6],[12,53.2],[13,53.2],[18,47.6],[16,47.6]], + [[6,64.4],[8,70],[9,70],[8,64.4],[6,64.4]], + [[23,82.6],[29,79.8],[30,79.8],[25,82.6],[23,82.6]], + [[37,70.7],[43,62.3],[44,62.3],[39,70.7],[37,70.7]], + [[48,51.1],[51,45.5],[53,45.5],[50,51.1],[48,51.1]], + [[51,35],[51,28.7],[53,28.7],[53,35],[51,35]], + [[52,22.4],[55,17.5],[56,17.5],[53,22.4],[52,22.4]], + [[58,12.6],[62,7],[63,7],[60,12.6],[58,12.6]], + [[0,3.5],[0,93.1],[64,93.1],[64,0],[63,0],[63,92.4], + [1,92.4],[1,3.5],[0,3.5]] + ]; + for (var i = 0; i < points.length; i++) { + for (var k = 0; k < points[i].length; k++) { + points[i][k][0] /= 10.5; + points[i][k][1] /= -10.5 / 0.75; + + points[i][k][0] += geoCoord[0]; + points[i][k][1] += geoCoord[1]; + } + } + module.exports = function (geo) { + if (geo.map === 'china') { + geo.regions.push(new Region( + '南海诸岛', points, geoCoord + )); + } + }; + + +/***/ }, +/* 170 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var coordsOffsetMap = { + '南海诸岛' : [32, 80], + // 全国 + '广东': [0, -10], + '香港': [10, 5], + '澳门': [-10, 10], + //'北京': [-10, 0], + '天津': [5, 5] + }; + + module.exports = function (geo) { + zrUtil.each(geo.regions, function (region) { + var coordFix = coordsOffsetMap[region.name]; + if (coordFix) { + var cp = region.center; + cp[0] += coordFix[0] / 10.5; + cp[1] += -coordFix[1] / (10.5 / 0.75); + } + }); + }; + + +/***/ }, +/* 171 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var geoCoordMap = { + 'Russia': [100, 60], + 'United States of America': [-99, 38] + }; + + module.exports = function (geo) { + zrUtil.each(geo.regions, function (region) { + var geoCoord = geoCoordMap[region.name]; + if (geoCoord) { + var cp = region.center; + cp[0] = geoCoord[0]; + cp[1] = geoCoord[1]; + } + }); + }; + + +/***/ }, +/* 172 */ +/***/ function(module, exports, __webpack_require__) { + + + + // var zrUtil = require('zrender/lib/core/util'); + var graphic = __webpack_require__(43); + + var MapDraw = __webpack_require__(173); + + __webpack_require__(1).extendChartView({ + + type: 'map', + + render: function (mapModel, ecModel, api, payload) { + // Not render if it is an toggleSelect action from self + if (payload && payload.type === 'mapToggleSelect' + && payload.from === this.uid + ) { + return; + } + + var group = this.group; + group.removeAll(); + // Not update map if it is an roam action from self + if (!(payload && payload.type === 'geoRoam' + && payload.componentType === 'series' + && payload.seriesId === mapModel.id)) { + + if (mapModel.needsDrawMap) { + var mapDraw = this._mapDraw || new MapDraw(api, true); + group.add(mapDraw.group); + + mapDraw.draw(mapModel, ecModel, api, this, payload); + + this._mapDraw = mapDraw; + } + else { + // Remove drawed map + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + } + } + else { + var mapDraw = this._mapDraw; + mapDraw && group.add(mapDraw.group); + } + + mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') + && this._renderSymbols(mapModel, ecModel, api); + }, + + remove: function () { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + this.group.removeAll(); + }, + + _renderSymbols: function (mapModel, ecModel, api) { + var originalData = mapModel.originalData; + var group = this.group; + + originalData.each('value', function (value, idx) { + if (isNaN(value)) { + return; + } + + var layout = originalData.getItemLayout(idx); + + if (!layout || !layout.point) { + // Not exists in map + return; + } + + var point = layout.point; + var offset = layout.offset; + + var circle = new graphic.Circle({ + style: { + // Because the special of map draw. + // Which needs statistic of multiple series and draw on one map. + // And each series also need a symbol with legend color + // + // Layout and visual are put one the different data + fill: mapModel.getData().getVisual('color') + }, + shape: { + cx: point[0] + offset * 9, + cy: point[1], + r: 3 + }, + silent: true, + z2: 10 + }); + + // First data on the same region + if (!offset) { + var fullData = mapModel.mainSeries.getData(); + var name = originalData.getName(idx); + var labelText = name; + var fullIndex = fullData.indexOfName(name); + + var itemModel = originalData.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var hoverLabelModel = itemModel.getModel('label.emphasis'); + + var textStyleModel = labelModel.getModel('textStyle'); + var hoverTextStyleModel = hoverLabelModel.getModel('textStyle'); + + var polygonGroups = fullData.getItemGraphicEl(fullIndex); + circle.setStyle({ + textPosition: 'bottom' + }); + + var onEmphasis = function () { + circle.setStyle({ + text: hoverLabelModel.get('show') ? labelText : '', + textFill: hoverTextStyleModel.getTextColor(), + textFont: hoverTextStyleModel.getFont() + }); + }; + + var onNormal = function () { + circle.setStyle({ + text: labelModel.get('show') ? labelText : '', + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + }); + }; + + polygonGroups.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + + onNormal(); + } + + group.add(circle); + }); + } + }); + + +/***/ }, +/* 173 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/helper/MapDraw + */ + + + var RoamController = __webpack_require__(174); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + function getFixedItemStyle(model, scale) { + var itemStyle = model.getItemStyle(); + var areaColor = model.get('areaColor'); + if (areaColor) { + itemStyle.fill = areaColor; + } + + return itemStyle; + } + + function updateMapSelectHandler(mapOrGeoModel, group, api, fromView) { + group.off('click'); + mapOrGeoModel.get('selectedMode') + && group.on('click', function (e) { + var el = e.target; + while (!el.__region) { + el = el.parent; + } + if (!el) { + return; + } + + var region = el.__region; + var action = { + type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect', + name: region.name, + from: fromView.uid + }; + action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id; + + api.dispatchAction(action); + + updateMapSelected(mapOrGeoModel, group); + }); + } + + function updateMapSelected(mapOrGeoModel, group) { + // FIXME + group.eachChild(function (otherRegionEl) { + if (otherRegionEl.__region) { + otherRegionEl.trigger(mapOrGeoModel.isSelected(otherRegionEl.__region.name) ? 'emphasis' : 'normal'); + } + }); + } + + /** + * @alias module:echarts/component/helper/MapDraw + * @param {module:echarts/ExtensionAPI} api + * @param {boolean} updateGroup + */ + function MapDraw(api, updateGroup) { + + var group = new graphic.Group(); + + /** + * @type {module:echarts/component/helper/RoamController} + * @private + */ + this._controller = new RoamController( + api.getZr(), updateGroup ? group : null, null + ); + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = group; + + /** + * @type {boolean} + * @private + */ + this._updateGroup = updateGroup; + } + + MapDraw.prototype = { + + constructor: MapDraw, + + draw: function (mapOrGeoModel, ecModel, api, fromView, payload) { + + // geoModel has no data + var data = mapOrGeoModel.getData && mapOrGeoModel.getData(); + + var geo = mapOrGeoModel.coordinateSystem; + + var group = this.group; + + var scale = geo.scale; + var groupNewProp = { + position: geo.position, + scale: scale + }; + + // No animation when first draw or in action + if (!group.childAt(0) || payload) { + group.attr(groupNewProp); + } + else { + graphic.updateProps(group, groupNewProp, mapOrGeoModel); + } + + group.removeAll(); + + var itemStyleAccessPath = ['itemStyle', 'normal']; + var hoverItemStyleAccessPath = ['itemStyle', 'emphasis']; + var labelAccessPath = ['label', 'normal']; + var hoverLabelAccessPath = ['label', 'emphasis']; + + zrUtil.each(geo.regions, function (region) { + + var regionGroup = new graphic.Group(); + var compoundPath = new graphic.CompoundPath({ + shape: { + paths: [] + } + }); + regionGroup.add(compoundPath); + + var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel; + + var itemStyleModel = regionModel.getModel(itemStyleAccessPath); + var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath); + var itemStyle = getFixedItemStyle(itemStyleModel, scale); + var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel, scale); + + var labelModel = regionModel.getModel(labelAccessPath); + var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath); + + var dataIdx; + // Use the itemStyle in data if has data + if (data) { + dataIdx = data.indexOfName(region.name); + // Only visual color of each item will be used. It can be encoded by dataRange + // But visual color of series is used in symbol drawing + // + // Visual color for each series is for the symbol draw + var visualColor = data.getItemVisual(dataIdx, 'color', true); + if (visualColor) { + itemStyle.fill = visualColor; + } + } + + var textStyleModel = labelModel.getModel('textStyle'); + var hoverTextStyleModel = hoverLabelModel.getModel('textStyle'); + + zrUtil.each(region.contours, function (contour) { + + var polygon = new graphic.Polygon({ + shape: { + points: contour + } + }); + + compoundPath.shape.paths.push(polygon); + }); + + compoundPath.setStyle(itemStyle); + compoundPath.style.strokeNoScale = true; + compoundPath.culling = true; + // Label + var showLabel = labelModel.get('show'); + var hoverShowLabel = hoverLabelModel.get('show'); + + var isDataNaN = data && isNaN(data.get('value', dataIdx)); + var itemLayout = data && data.getItemLayout(dataIdx); + // In the following cases label will be drawn + // 1. In map series and data value is NaN + // 2. In geo component + // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout + if ( + (!data || isDataNaN && (showLabel || hoverShowLabel)) + || (itemLayout && itemLayout.showLabel) + ) { + var query = data ? dataIdx : region.name; + var formattedStr = mapOrGeoModel.getFormattedLabel(query, 'normal'); + var hoverFormattedStr = mapOrGeoModel.getFormattedLabel(query, 'emphasis'); + var text = new graphic.Text({ + style: { + text: showLabel ? (formattedStr || region.name) : '', + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textAlign: 'center', + textVerticalAlign: 'middle' + }, + hoverStyle: { + text: hoverShowLabel ? (hoverFormattedStr || region.name) : '', + fill: hoverTextStyleModel.getTextColor(), + textFont: hoverTextStyleModel.getFont() + }, + position: region.center.slice(), + scale: [1 / scale[0], 1 / scale[1]], + z2: 10, + silent: true + }); + + regionGroup.add(text); + } + + // setItemGraphicEl, setHoverStyle after all polygons and labels + // are added to the rigionGroup + if (data) { + data.setItemGraphicEl(dataIdx, regionGroup); + } + else { + var regionModel = mapOrGeoModel.getRegionModel(region.name); + // Package custom mouse event for geo component + compoundPath.eventData = { + componentType: 'geo', + geoIndex: mapOrGeoModel.componentIndex, + name: region.name, + region: (regionModel && regionModel.option) || {} + }; + } + + regionGroup.__region = region; + + graphic.setHoverStyle(regionGroup, hoverItemStyle); + + group.add(regionGroup); + }); + + this._updateController(mapOrGeoModel, ecModel, api); + + updateMapSelectHandler(mapOrGeoModel, group, api, fromView); + + updateMapSelected(mapOrGeoModel, group); + }, + + remove: function () { + this.group.removeAll(); + this._controller.dispose(); + }, + + _updateController: function (mapOrGeoModel, ecModel, api) { + var geo = mapOrGeoModel.coordinateSystem; + var controller = this._controller; + controller.zoomLimit = mapOrGeoModel.get('scaleLimit'); + // Update zoom from model + controller.zoom = geo.getZoom(); + // roamType is will be set default true if it is null + controller.enable(mapOrGeoModel.get('roam') || false); + var mainType = mapOrGeoModel.mainType; + + function makeActionBase() { + var action = { + type: 'geoRoam', + componentType: mainType + }; + action[mainType + 'Id'] = mapOrGeoModel.id; + return action; + } + controller.off('pan') + .on('pan', function (dx, dy) { + api.dispatchAction(zrUtil.extend(makeActionBase(), { + dx: dx, + dy: dy + })); + }); + controller.off('zoom') + .on('zoom', function (zoom, mouseX, mouseY) { + api.dispatchAction(zrUtil.extend(makeActionBase(), { + zoom: zoom, + originX: mouseX, + originY: mouseY + })); + + if (this._updateGroup) { + var group = this.group; + var scale = group.scale; + group.traverse(function (el) { + if (el.type === 'text') { + el.attr('scale', [1 / scale[0], 1 / scale[1]]); + } + }); + } + }, this); + + controller.rectProvider = function () { + return geo.getViewRectAfterRoam(); + }; + } + }; + + module.exports = MapDraw; + + +/***/ }, +/* 174 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/helper/RoamController + */ + + + + var Eventful = __webpack_require__(33); + var zrUtil = __webpack_require__(4); + var eventTool = __webpack_require__(87); + var interactionMutex = __webpack_require__(175); + + function mousedown(e) { + if (e.target && e.target.draggable) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + var rect = this.rectProvider && this.rectProvider(); + if (rect && rect.contain(x, y)) { + this._x = x; + this._y = y; + this._dragging = true; + } + } + + function mousemove(e) { + if (!this._dragging) { + return; + } + + eventTool.stop(e.event); + + if (e.gestureEvent !== 'pinch') { + + if (interactionMutex.isTaken(this._zr, 'globalPan')) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + + var dx = x - this._x; + var dy = y - this._y; + + this._x = x; + this._y = y; + + var target = this.target; + + if (target) { + var pos = target.position; + pos[0] += dx; + pos[1] += dy; + target.dirty(); + } + + eventTool.stop(e.event); + this.trigger('pan', dx, dy); + } + } + + function mouseup(e) { + this._dragging = false; + } + + function mousewheel(e) { + // Convenience: + // Mac and VM Windows on Mac: scroll up: zoom out. + // Windows: scroll up: zoom in. + var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1; + zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY); + } + + function pinch(e) { + if (interactionMutex.isTaken(this._zr, 'globalPan')) { + return; + } + var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1; + zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY); + } + + function zoom(e, zoomDelta, zoomX, zoomY) { + var rect = this.rectProvider && this.rectProvider(); + + if (rect && rect.contain(zoomX, zoomY)) { + // When mouse is out of roamController rect, + // default befavoius should be be disabled, otherwise + // page sliding is disabled, contrary to expectation. + eventTool.stop(e.event); + + var target = this.target; + var zoomLimit = this.zoomLimit; + + if (target) { + var pos = target.position; + var scale = target.scale; + + var newZoom = this.zoom = this.zoom || 1; + newZoom *= zoomDelta; + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + newZoom = Math.max( + Math.min(zoomMax, newZoom), + zoomMin + ); + } + var zoomScale = newZoom / this.zoom; + this.zoom = newZoom; + // Keep the mouse center when scaling + pos[0] -= (zoomX - pos[0]) * (zoomScale - 1); + pos[1] -= (zoomY - pos[1]) * (zoomScale - 1); + scale[0] *= zoomScale; + scale[1] *= zoomScale; + + target.dirty(); + } + + this.trigger('zoom', zoomDelta, zoomX, zoomY); + } + } + + /** + * @alias module:echarts/component/helper/RoamController + * @constructor + * @mixin {module:zrender/mixin/Eventful} + * + * @param {module:zrender/zrender~ZRender} zr + * @param {module:zrender/Element} target + * @param {Function} [rectProvider] + */ + function RoamController(zr, target, rectProvider) { + + /** + * @type {module:zrender/Element} + */ + this.target = target; + + /** + * @type {Function} + */ + this.rectProvider = rectProvider; + + /** + * { min: 1, max: 2 } + * @type {Object} + */ + this.zoomLimit; + + /** + * @type {number} + */ + this.zoom; + /** + * @type {module:zrender} + */ + this._zr = zr; + + // Avoid two roamController bind the same handler + var bind = zrUtil.bind; + var mousedownHandler = bind(mousedown, this); + var mousemoveHandler = bind(mousemove, this); + var mouseupHandler = bind(mouseup, this); + var mousewheelHandler = bind(mousewheel, this); + var pinchHandler = bind(pinch, this); + + Eventful.call(this); + + /** + * Notice: only enable needed types. For example, if 'zoom' + * is not needed, 'zoom' should not be enabled, otherwise + * default mousewheel behaviour (scroll page) will be disabled. + * + * @param {boolean|string} [controlType=true] Specify the control type, + * which can be null/undefined or true/false + * or 'pan/move' or 'zoom'/'scale' + */ + this.enable = function (controlType) { + // Disable previous first + this.disable(); + + if (controlType == null) { + controlType = true; + } + + if (controlType === true || (controlType === 'move' || controlType === 'pan')) { + zr.on('mousedown', mousedownHandler); + zr.on('mousemove', mousemoveHandler); + zr.on('mouseup', mouseupHandler); + } + if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) { + zr.on('mousewheel', mousewheelHandler); + zr.on('pinch', pinchHandler); + } + }; + + this.disable = function () { + zr.off('mousedown', mousedownHandler); + zr.off('mousemove', mousemoveHandler); + zr.off('mouseup', mouseupHandler); + zr.off('mousewheel', mousewheelHandler); + zr.off('pinch', pinchHandler); + }; + + this.dispose = this.disable; + + this.isDragging = function () { + return this._dragging; + }; + + this.isPinching = function () { + return this._pinching; + }; + } + + zrUtil.mixin(RoamController, Eventful); + + module.exports = RoamController; + + +/***/ }, +/* 175 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ATTR = '\0_ec_interaction_mutex'; + + var interactionMutex = { + + take: function (zr, resourceKey, userKey) { + var store = getStore(zr); + store[resourceKey] = userKey; + }, + + release: function (zr, resourceKey, userKey) { + var store = getStore(zr); + var uKey = store[resourceKey]; + + if (uKey === userKey) { + store[resourceKey] = null; + } + }, + + isTaken: function (zr, resourceKey) { + return !!getStore(zr)[resourceKey]; + } + }; + + function getStore(zr) { + return zr[ATTR] || (zr[ATTR] = {}); + } + + /** + * payload: { + * type: 'takeGlobalCursor', + * key: 'dataZoomSelect', or 'brush', or ..., + * If no userKey, release global cursor. + * } + */ + __webpack_require__(1).registerAction( + {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'}, + function () {} + ); + + module.exports = interactionMutex; + + +/***/ }, +/* 176 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var roamHelper = __webpack_require__(177); + + var echarts = __webpack_require__(1); + + /** + * @payload + * @property {string} [componentType=series] + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + echarts.registerAction({ + type: 'geoRoam', + event: 'geoRoam', + update: 'updateLayout' + }, function (payload, ecModel) { + var componentType = payload.componentType || 'series'; + + ecModel.eachComponent( + { mainType: componentType, query: payload }, + function (componentModel) { + var geo = componentModel.coordinateSystem; + if (geo.type !== 'geo') { + return; + } + + var res = roamHelper.updateCenterAndZoom( + geo, payload, componentModel.get('scaleLimit') + ); + + componentModel.setCenter + && componentModel.setCenter(res.center); + + componentModel.setZoom + && componentModel.setZoom(res.zoom); + + // All map series with same `map` use the same geo coordinate system + // So the center and zoom must be in sync. Include the series not selected by legend + if (componentType === 'series') { + zrUtil.each(componentModel.seriesGroup, function (seriesModel) { + seriesModel.setCenter(res.center); + seriesModel.setZoom(res.zoom); + }); + } + } + ); + }); + + +/***/ }, +/* 177 */ +/***/ function(module, exports) { + + + + var roamHelper = {}; + + /** + * @param {module:echarts/coord/View} view + * @param {Object} payload + * @param {Object} [zoomLimit] + */ + roamHelper.updateCenterAndZoom = function ( + view, payload, zoomLimit + ) { + var previousZoom = view.getZoom(); + var center = view.getCenter(); + var zoom = payload.zoom; + + var point = view.dataToPoint(center); + + if (payload.dx != null && payload.dy != null) { + point[0] -= payload.dx; + point[1] -= payload.dy; + + var center = view.pointToData(point); + view.setCenter(center); + } + if (zoom != null) { + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + zoom = Math.max( + Math.min(previousZoom * zoom, zoomMax), + zoomMin + ) / previousZoom; + } + + // Zoom on given point(originX, originY) + view.scale[0] *= zoom; + view.scale[1] *= zoom; + var position = view.position; + var fixX = (payload.originX - position[0]) * (zoom - 1); + var fixY = (payload.originY - position[1]) * (zoom - 1); + + position[0] -= fixX; + position[1] -= fixY; + + view.updateTransform(); + // Get the new center + var center = view.pointToData(point); + view.setCenter(center); + view.setZoom(zoom * previousZoom); + } + + return { + center: view.getCenter(), + zoom: view.getZoom() + }; + }; + + module.exports = roamHelper; + + +/***/ }, +/* 178 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel) { + + var processedMapType = {}; + + ecModel.eachSeriesByType('map', function (mapSeries) { + var mapType = mapSeries.get('map'); + if (processedMapType[mapType]) { + return; + } + + var mapSymbolOffsets = {}; + + zrUtil.each(mapSeries.seriesGroup, function (subMapSeries) { + var geo = subMapSeries.coordinateSystem; + var data = subMapSeries.originalData; + if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) { + data.each('value', function (value, idx) { + var name = data.getName(idx); + var region = geo.getRegion(name); + + // No region or no value + // In MapSeries data regions will be filled with NaN + // If they are not in the series.data array. + // So here must validate if value is NaN + if (!region || isNaN(value)) { + return; + } + + var offset = mapSymbolOffsets[name] || 0; + + var point = geo.dataToPoint(region.center); + + mapSymbolOffsets[name] = offset + 1; + + data.setItemLayout(idx, { + point: point, + offset: offset + }); + }); + } + }); + + // Show label of those region not has legendSymbol(which is offset 0) + var data = mapSeries.getData(); + data.each(function (idx) { + var name = data.getName(idx); + var layout = data.getItemLayout(idx) || {}; + layout.showLabel = !mapSymbolOffsets[name]; + data.setItemLayout(idx, layout); + }); + + processedMapType[mapType] = true; + }); + }; + + +/***/ }, +/* 179 */ +/***/ function(module, exports) { + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('map', function (seriesModel) { + var colorList = seriesModel.get('color'); + var itemStyleModel = seriesModel.getModel('itemStyle.normal'); + + var areaColor = itemStyleModel.get('areaColor'); + var color = itemStyleModel.get('color') + || colorList[seriesModel.seriesIndex % colorList.length]; + + seriesModel.getData().setVisual({ + 'areaColor': areaColor, + 'color': color + }); + }); + }; + + +/***/ }, +/* 180 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + // FIXME 公用? + /** + * @param {Array.} datas + * @param {string} statisticType 'average' 'sum' + * @inner + */ + function dataStatistics(datas, statisticType) { + var dataNameMap = {}; + var dims = ['value']; + + for (var i = 0; i < datas.length; i++) { + datas[i].each(dims, function (value, idx) { + var name = datas[i].getName(idx); + dataNameMap[name] = dataNameMap[name] || []; + if (!isNaN(value)) { + dataNameMap[name].push(value); + } + }); + } + + return datas[0].map(dims, function (value, idx) { + var name = datas[0].getName(idx); + var sum = 0; + var min = Infinity; + var max = -Infinity; + var len = dataNameMap[name].length; + for (var i = 0; i < len; i++) { + min = Math.min(min, dataNameMap[name][i]); + max = Math.max(max, dataNameMap[name][i]); + sum += dataNameMap[name][i]; + } + var result; + if (statisticType === 'min') { + result = min; + } + else if (statisticType === 'max') { + result = max; + } + else if (statisticType === 'average') { + result = sum / len; + } + else { + result = sum; + } + return len === 0 ? NaN : result; + }); + } + + module.exports = function (ecModel) { + var seriesGroupByMapType = {}; + ecModel.eachSeriesByType('map', function (seriesModel) { + var mapType = seriesModel.get('map'); + seriesGroupByMapType[mapType] = seriesGroupByMapType[mapType] || []; + seriesGroupByMapType[mapType].push(seriesModel); + }); + + zrUtil.each(seriesGroupByMapType, function (seriesList, mapType) { + var data = dataStatistics( + zrUtil.map(seriesList, function (seriesModel) { + return seriesModel.getData(); + }), + seriesList[0].get('mapValueCalculation') + ); + + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].originalData = seriesList[i].getData(); + } + + // FIXME Put where? + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].seriesGroup = seriesList; + seriesList[i].needsDrawMap = i === 0; + + seriesList[i].setData(data.cloneShallow()); + seriesList[i].mainSeries = seriesList[0]; + } + }); + }; + + +/***/ }, +/* 181 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + // Save geoCoord + var mapSeries = []; + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt.type === 'map') { + mapSeries.push(seriesOpt); + } + }); + + zrUtil.each(mapSeries, function (seriesOpt) { + seriesOpt.map = seriesOpt.map || seriesOpt.mapType; + // Put x, y, width, height, x2, y2 in the top level + zrUtil.defaults(seriesOpt, seriesOpt.mapLocation); + }); + }; + + +/***/ }, +/* 182 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(183); + __webpack_require__(186); + __webpack_require__(190); + + echarts.registerVisual(__webpack_require__(191)); + + echarts.registerLayout(__webpack_require__(193)); + + +/***/ }, +/* 183 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SeriesModel = __webpack_require__(28); + var Tree = __webpack_require__(184); + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + + module.exports = SeriesModel.extend({ + + type: 'series.treemap', + + dependencies: ['grid', 'polar'], + + /** + * @type {module:echarts/data/Tree~Node} + */ + _viewRoot: null, + + defaultOption: { + // Disable progressive rendering + progressive: 0, + hoverLayerThreshold: Infinity, + // center: ['50%', '50%'], // not supported in ec3. + // size: ['80%', '80%'], // deprecated, compatible with ec2. + left: 'center', + top: 'middle', + right: null, + bottom: null, + width: '80%', + height: '80%', + sort: true, // Can be null or false or true + // (order by desc default, asc not supported yet (strange effect)) + clipWindow: 'origin', // Size of clipped window when zooming. 'origin' or 'fullscreen' + squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio + leafDepth: null, // Nodes on depth from root are regarded as leaves. + // Count from zero (zero represents only view root). + drillDownIcon: '▶', // Use html character temporarily because it is complicated + // to align specialized icon. ▷▶❒❐▼✚ + visualDimension: 0, // Can be 0, 1, 2, 3. + zoomToNodeRatio: 0.32 * 0.32, // Be effective when using zoomToNode. Specify the proportion of the + // target node area in the view area. + roam: true, // true, false, 'scale' or 'zoom', 'move'. + nodeClick: 'zoomToNode', // Leaf node click behaviour: 'zoomToNode', 'link', false. + // If leafDepth is set and clicking a node which has children but + // be on left depth, the behaviour would be changing root. Otherwise + // use behavious defined above. + animation: true, + animationDurationUpdate: 900, + animationEasing: 'quinticInOut', + breadcrumb: { + show: true, + height: 22, + left: 'center', + top: 'bottom', + // right + // bottom + emptyItemWidth: 25, // Width of empty node. + itemStyle: { + normal: { + color: 'rgba(0,0,0,0.7)', //'#5793f3', + borderColor: 'rgba(255,255,255,0.7)', + borderWidth: 1, + shadowColor: 'rgba(150,150,150,1)', + shadowBlur: 3, + shadowOffsetX: 0, + shadowOffsetY: 0, + textStyle: { + color: '#fff' + } + }, + emphasis: { + textStyle: {} + } + } + }, + label: { + normal: { + show: true, + position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ... + textStyle: { + color: '#fff', + ellipsis: true + } + } + }, + itemStyle: { + normal: { + color: null, // Can be 'none' if not necessary. + colorAlpha: null, // Can be 'none' if not necessary. + colorSaturation: null, // Can be 'none' if not necessary. + borderWidth: 0, + gapWidth: 0, + borderColor: '#fff', + borderColorSaturation: null // If specified, borderColor will be ineffective, and the + // border color is evaluated by color of current node and + // borderColorSaturation. + }, + emphasis: { + + } + }, + color: [], // + treemapSeries.color should not be modified. Please only modified + // level[n].color (if necessary). + // + Specify color list of each level. level[0].color would be global + // color list if not specified. (see method `setDefault`). + // + But set as a empty array to forbid fetch color from global palette + // when using nodeModel.get('color'), otherwise nodes on deep level + // will always has color palette set and are not able to inherit color + // from parent node. + // + TreemapSeries.color can not be set as 'none', otherwise effect + // legend color fetching (see seriesColor.js). + colorAlpha: null, // Array. Specify color alpha range of each level, like [0.2, 0.8] + colorSaturation: null, // Array. Specify color saturation of each level, like [0.2, 0.5] + colorMappingBy: 'index', // 'value' or 'index' or 'id'. + visibleMin: 10, // If area less than this threshold (unit: pixel^2), node will not + // be rendered. Only works when sort is 'asc' or 'desc'. + childrenVisibleMin: null, // If area of a node less than this threshold (unit: pixel^2), + // grandchildren will not show. + // Why grandchildren? If not grandchildren but children, + // some siblings show children and some not, + // the appearance may be mess and not consistent, + levels: [] // Each item: { + // visibleMin, itemStyle, visualDimension, label + // } + // data: { + // value: [], + // children: [], + // link: 'http://xxx.xxx.xxx', + // target: 'blank' or 'self' + // } + }, + + /** + * @override + */ + getInitialData: function (option, ecModel) { + var data = option.data || []; + var rootName = option.name; + rootName == null && (rootName = option.name); + + // Create a virtual root. + var root = {name: rootName, children: option.data}; + var value0 = (data[0] || {}).value; + + completeTreeValue(root, zrUtil.isArray(value0) ? value0.length : -1); + + // FIXME + // sereis.mergeOption 的 getInitData是否放在merge后,从而能直接获取merege后的结果而非手动判断。 + var levels = option.levels || []; + + levels = option.levels = setDefault(levels, ecModel); + + // Make sure always a new tree is created when setOption, + // in TreemapView, we check whether oldTree === newTree + // to choose mappings approach among old shapes and new shapes. + return Tree.createTree(root, this, levels).data; + }, + + optionUpdated: function () { + this.resetViewRoot(); + }, + + /** + * @override + * @param {number} dataIndex + * @param {boolean} [mutipleSeries=false] + */ + formatTooltip: function (dataIndex) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? addCommas(value[0]) : addCommas(value); + var name = data.getName(dataIndex); + + return encodeHTML(name) + ': ' + formattedValue; + }, + + /** + * Add tree path to tooltip param + * + * @override + * @param {number} dataIndex + * @return {Object} + */ + getDataParams: function (dataIndex) { + var params = SeriesModel.prototype.getDataParams.apply(this, arguments); + + var data = this.getData(); + var node = data.tree.getNodeByDataIndex(dataIndex); + var treePathInfo = params.treePathInfo = []; + + while (node) { + var nodeDataIndex = node.dataIndex; + treePathInfo.push({ + name: node.name, + dataIndex: nodeDataIndex, + value: this.getRawValue(nodeDataIndex) + }); + node = node.parentNode; + } + + treePathInfo.reverse(); + + return params; + }, + + /** + * @public + * @param {Object} layoutInfo { + * x: containerGroup x + * y: containerGroup y + * width: containerGroup width + * height: containerGroup height + * } + */ + setLayoutInfo: function (layoutInfo) { + /** + * @readOnly + * @type {Object} + */ + this.layoutInfo = this.layoutInfo || {}; + zrUtil.extend(this.layoutInfo, layoutInfo); + }, + + /** + * @param {string} id + * @return {number} index + */ + mapIdToIndex: function (id) { + // A feature is implemented: + // index is monotone increasing with the sequence of + // input id at the first time. + // This feature can make sure that each data item and its + // mapped color have the same index between data list and + // color list at the beginning, which is useful for user + // to adjust data-color mapping. + + /** + * @private + * @type {Object} + */ + var idIndexMap = this._idIndexMap; + + if (!idIndexMap) { + idIndexMap = this._idIndexMap = {}; + /** + * @private + * @type {number} + */ + this._idIndexMapCount = 0; + } + + var index = idIndexMap[id]; + if (index == null) { + idIndexMap[id] = index = this._idIndexMapCount++; + } + + return index; + }, + + getViewRoot: function () { + return this._viewRoot; + }, + + /** + * @param {module:echarts/data/Tree~Node} [viewRoot] + */ + resetViewRoot: function (viewRoot) { + viewRoot + ? (this._viewRoot = viewRoot) + : (viewRoot = this._viewRoot); + + var root = this.getData().tree.root; + + if (!viewRoot + || (viewRoot !== root && !root.contains(viewRoot)) + ) { + this._viewRoot = root; + } + } + }); + + /** + * @param {Object} dataNode + */ + function completeTreeValue(dataNode, arrValueLength) { + // Postorder travel tree. + // If value of none-leaf node is not set, + // calculate it by suming up the value of all children. + var sum = 0; + + zrUtil.each(dataNode.children, function (child) { + + completeTreeValue(child, arrValueLength); + + var childValue = child.value; + zrUtil.isArray(childValue) && (childValue = childValue[0]); + + sum += childValue; + }); + + var thisValue = dataNode.value; + + if (arrValueLength >= 0) { + if (!zrUtil.isArray(thisValue)) { + dataNode.value = new Array(arrValueLength); + } + else { + thisValue = thisValue[0]; + } + } + + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + + arrValueLength >= 0 + ? (dataNode.value[0] = thisValue) + : (dataNode.value = thisValue); + } + + /** + * set default to level configuration + */ + function setDefault(levels, ecModel) { + var globalColorList = ecModel.get('color'); + + if (!globalColorList) { + return; + } + + levels = levels || []; + var hasColorDefine; + zrUtil.each(levels, function (levelDefine) { + var model = new Model(levelDefine); + var modelColor = model.get('color'); + + if (model.get('itemStyle.normal.color') + || (modelColor && modelColor !== 'none') + ) { + hasColorDefine = true; + } + }); + + if (!hasColorDefine) { + var level0 = levels[0] || (levels[0] = {}); + level0.color = globalColorList.slice(); + } + + return levels; + } + + + +/***/ }, +/* 184 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Tree data structure + * + * @module echarts/data/Tree + */ + + + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + var List = __webpack_require__(97); + var linkList = __webpack_require__(185); + var completeDimensions = __webpack_require__(102); + + /** + * @constructor module:echarts/data/Tree~TreeNode + * @param {string} name + * @param {module:echarts/data/Tree} hostTree + */ + var TreeNode = function (name, hostTree) { + /** + * @type {string} + */ + this.name = name || ''; + + /** + * Depth of node + * + * @type {number} + * @readOnly + */ + this.depth = 0; + + /** + * Height of the subtree rooted at this node. + * @type {number} + * @readOnly + */ + this.height = 0; + + /** + * @type {module:echarts/data/Tree~TreeNode} + * @readOnly + */ + this.parentNode = null; + + /** + * Reference to list item. + * Do not persistent dataIndex outside, + * besause it may be changed by list. + * If dataIndex -1, + * this node is logical deleted (filtered) in list. + * + * @type {Object} + * @readOnly + */ + this.dataIndex = -1; + + /** + * @type {Array.} + * @readOnly + */ + this.children = []; + + /** + * @type {Array.} + * @pubilc + */ + this.viewChildren = []; + + /** + * @type {moduel:echarts/data/Tree} + * @readOnly + */ + this.hostTree = hostTree; + }; + + TreeNode.prototype = { + + constructor: TreeNode, + + /** + * The node is removed. + * @return {boolean} is removed. + */ + isRemoved: function () { + return this.dataIndex < 0; + }, + + /** + * Travel this subtree (include this node). + * Usage: + * node.eachNode(function () { ... }); // preorder + * node.eachNode('preorder', function () { ... }); // preorder + * node.eachNode('postorder', function () { ... }); // postorder + * node.eachNode( + * {order: 'postorder', attr: 'viewChildren'}, + * function () { ... } + * ); // postorder + * + * @param {(Object|string)} options If string, means order. + * @param {string=} options.order 'preorder' or 'postorder' + * @param {string=} options.attr 'children' or 'viewChildren' + * @param {Function} cb If in preorder and return false, + * its subtree will not be visited. + * @param {Object} [context] + */ + eachNode: function (options, cb, context) { + if (typeof options === 'function') { + context = cb; + cb = options; + options = null; + } + + options = options || {}; + if (zrUtil.isString(options)) { + options = {order: options}; + } + + var order = options.order || 'preorder'; + var children = this[options.attr || 'children']; + + var suppressVisitSub; + order === 'preorder' && (suppressVisitSub = cb.call(context, this)); + + for (var i = 0; !suppressVisitSub && i < children.length; i++) { + children[i].eachNode(options, cb, context); + } + + order === 'postorder' && cb.call(context, this); + }, + + /** + * Update depth and height of this subtree. + * + * @param {number} depth + */ + updateDepthAndHeight: function (depth) { + var height = 0; + this.depth = depth; + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + child.updateDepthAndHeight(depth + 1); + if (child.height > height) { + height = child.height; + } + } + this.height = height + 1; + }, + + /** + * @param {string} id + * @return {module:echarts/data/Tree~TreeNode} + */ + getNodeById: function (id) { + if (this.getId() === id) { + return this; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].getNodeById(id); + if (res) { + return res; + } + } + }, + + /** + * @param {module:echarts/data/Tree~TreeNode} node + * @return {boolean} + */ + contains: function (node) { + if (node === this) { + return true; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].contains(node); + if (res) { + return res; + } + } + }, + + /** + * @param {boolean} includeSelf Default false. + * @return {Array.} order: [root, child, grandchild, ...] + */ + getAncestors: function (includeSelf) { + var ancestors = []; + var node = includeSelf ? this : this.parentNode; + while (node) { + ancestors.push(node); + node = node.parentNode; + } + ancestors.reverse(); + return ancestors; + }, + + /** + * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3 + * @return {number} Value. + */ + getValue: function (dimension) { + var data = this.hostTree.data; + return data.get(data.getDimension(dimension || 'value'), this.dataIndex); + }, + + /** + * @param {Object} layout + * @param {boolean=} [merge=false] + */ + setLayout: function (layout, merge) { + this.dataIndex >= 0 + && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge); + }, + + /** + * @return {Object} layout + */ + getLayout: function () { + return this.hostTree.data.getItemLayout(this.dataIndex); + }, + + /** + * @param {string} path + * @return {module:echarts/model/Model} + */ + getModel: function (path) { + if (this.dataIndex < 0) { + return; + } + var hostTree = this.hostTree; + var itemModel = hostTree.data.getItemModel(this.dataIndex); + var levelModel = this.getLevelModel(); + + return itemModel.getModel(path, (levelModel || hostTree.hostModel).getModel(path)); + }, + + /** + * @return {module:echarts/model/Model} + */ + getLevelModel: function () { + return (this.hostTree.levelModels || [])[this.depth]; + }, + + /** + * @example + * setItemVisual('color', color); + * setItemVisual({ + * 'color': color + * }); + */ + setVisual: function (key, value) { + this.dataIndex >= 0 + && this.hostTree.data.setItemVisual(this.dataIndex, key, value); + }, + + /** + * Get item visual + */ + getVisual: function (key, ignoreParent) { + return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent); + }, + + /** + * @public + * @return {number} + */ + getRawIndex: function () { + return this.hostTree.data.getRawIndex(this.dataIndex); + }, + + /** + * @public + * @return {string} + */ + getId: function () { + return this.hostTree.data.getId(this.dataIndex); + } + }; + + /** + * @constructor + * @alias module:echarts/data/Tree + * @param {module:echarts/model/Model} hostModel + * @param {Array.} levelOptions + */ + function Tree(hostModel, levelOptions) { + /** + * @type {module:echarts/data/Tree~TreeNode} + * @readOnly + */ + this.root; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.data; + + /** + * Index of each item is the same as the raw index of coresponding list item. + * @private + * @type {Array.} levelOptions + * @return module:echarts/data/Tree + */ + Tree.createTree = function (dataRoot, hostModel, levelOptions) { + + var tree = new Tree(hostModel, levelOptions); + var listData = []; + + buildHierarchy(dataRoot); + + function buildHierarchy(dataNode, parentNode) { + listData.push(dataNode); + + var node = new TreeNode(dataNode.name, tree); + parentNode + ? addChild(node, parentNode) + : (tree.root = node); + + tree._nodes.push(node); + + var children = dataNode.children; + if (children) { + for (var i = 0; i < children.length; i++) { + buildHierarchy(children[i], node); + } + } + } + + tree.root.updateDepthAndHeight(0); + + var dimensions = completeDimensions([{name: 'value'}], listData); + var list = new List(dimensions, hostModel); + list.initData(listData); + + linkList({ + mainData: list, + struct: tree, + structAttr: 'tree' + }); + + tree.update(); + + return tree; + }; + + /** + * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote, + * so this function is not ready and not necessary to be public. + * + * @param {(module:echarts/data/Tree~TreeNode|Object)} child + */ + function addChild(child, node) { + var children = node.children; + if (child.parentNode === node) { + return; + } + + children.push(child); + child.parentNode = node; + } + + module.exports = Tree; + + +/***/ }, +/* 185 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Link lists and struct (graph or tree) + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + var DATAS = '\0__link_datas'; + var MAIN_DATA = '\0__link_mainData'; + + // Caution: + // In most case, either list or its shallow clones (see list.cloneShallow) + // is active in echarts process. So considering heap memory consumption, + // we do not clone tree or graph, but share them among list and its shallow clones. + // But in some rare case, we have to keep old list (like do animation in chart). So + // please take care that both the old list and the new list share the same tree/graph. + + /** + * @param {Object} opt + * @param {module:echarts/data/List} opt.mainData + * @param {Object} [opt.struct] For example, instance of Graph or Tree. + * @param {string} [opt.structAttr] designation: list[structAttr] = struct; + * @param {Object} [opt.datas] {dataType: data}, + * like: {node: nodeList, edge: edgeList}. + * Should contain mainData. + * @param {Object} [opt.datasAttr] {dataType: attr}, + * designation: struct[datasAttr[dataType]] = list; + */ + function linkList(opt) { + var mainData = opt.mainData; + var datas = opt.datas; + + if (!datas) { + datas = {main: mainData}; + opt.datasAttr = {main: 'data'}; + } + opt.datas = opt.mainData = null; + + linkAll(mainData, datas, opt); + + // Porxy data original methods. + each(datas, function (data) { + each(mainData.TRANSFERABLE_METHODS, function (methodName) { + data.wrapMethod(methodName, zrUtil.curry(transferInjection, opt)); + }); + + }); + + // Beyond transfer, additional features should be added to `cloneShallow`. + mainData.wrapMethod('cloneShallow', zrUtil.curry(cloneShallowInjection, opt)); + + // Only mainData trigger change, because struct.update may trigger + // another changable methods, which may bring about dead lock. + each(mainData.CHANGABLE_METHODS, function (methodName) { + mainData.wrapMethod(methodName, zrUtil.curry(changeInjection, opt)); + }); + + // Make sure datas contains mainData. + zrUtil.assert(datas[mainData.dataType] === mainData); + } + + function transferInjection(opt, res) { + if (isMainData(this)) { + // Transfer datas to new main data. + var datas = zrUtil.extend({}, this[DATAS]); + datas[this.dataType] = res; + linkAll(res, datas, opt); + } + else { + // Modify the reference in main data to point newData. + linkSingle(res, this.dataType, this[MAIN_DATA], opt); + } + return res; + } + + function changeInjection(opt, res) { + opt.struct && opt.struct.update(this); + return res; + } + + function cloneShallowInjection(opt, res) { + // cloneShallow, which brings about some fragilities, may be inappropriate + // to be exposed as an API. So for implementation simplicity we can make + // the restriction that cloneShallow of not-mainData should not be invoked + // outside, but only be invoked here. + each(res[DATAS], function (data, dataType) { + data !== res && linkSingle(data.cloneShallow(), dataType, res, opt); + }); + return res; + } + + /** + * Supplement method to List. + * + * @public + * @param {string} [dataType] If not specified, return mainData. + * @return {module:echarts/data/List} + */ + function getLinkedData(dataType) { + var mainData = this[MAIN_DATA]; + return (dataType == null || mainData == null) + ? mainData + : mainData[DATAS][dataType]; + } + + function isMainData(data) { + return data[MAIN_DATA] === data; + } + + function linkAll(mainData, datas, opt) { + mainData[DATAS] = {}; + each(datas, function (data, dataType) { + linkSingle(data, dataType, mainData, opt); + }); + } + + function linkSingle(data, dataType, mainData, opt) { + mainData[DATAS][dataType] = data; + data[MAIN_DATA] = mainData; + data.dataType = dataType; + + if (opt.struct) { + data[opt.structAttr] = opt.struct; + opt.struct[opt.datasAttr[dataType]] = data; + } + + // Supplement method. + data.getLinkedData = getLinkedData; + } + + module.exports = linkList; + + +/***/ }, +/* 186 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var DataDiffer = __webpack_require__(98); + var helper = __webpack_require__(187); + var Breadcrumb = __webpack_require__(188); + var RoamController = __webpack_require__(174); + var BoundingRect = __webpack_require__(9); + var matrix = __webpack_require__(11); + var animationUtil = __webpack_require__(189); + var bind = zrUtil.bind; + var Group = graphic.Group; + var Rect = graphic.Rect; + var each = zrUtil.each; + + var DRAG_THRESHOLD = 3; + var PATH_LABEL_NORMAL = ['label', 'normal']; + var PATH_LABEL_EMPHASIS = ['label', 'emphasis']; + var Z_BASE = 10; // Should bigger than every z. + var Z_BG = 1; + var Z_CONTENT = 2; + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'treemap', + + /** + * @override + */ + init: function (o, api) { + + /** + * @private + * @type {module:zrender/container/Group} + */ + this._containerGroup; + + /** + * @private + * @type {Object.>} + */ + this._storage = createStorage(); + + /** + * @private + * @type {module:echarts/data/Tree} + */ + this._oldTree; + + /** + * @private + * @type {module:echarts/chart/treemap/Breadcrumb} + */ + this._breadcrumb; + + /** + * @private + * @type {module:echarts/component/helper/RoamController} + */ + this._controller; + + /** + * 'ready', 'animating' + * @private + */ + this._state = 'ready'; + + /** + * @private + * @type {boolean} + */ + this._mayClick; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + + var models = ecModel.findComponents({ + mainType: 'series', subType: 'treemap', query: payload + }); + if (zrUtil.indexOf(models, seriesModel) < 0) { + return; + } + + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + + var targetInfo = helper.retrieveTargetInfo(payload, seriesModel); + var payloadType = payload && payload.type; + var layoutInfo = seriesModel.layoutInfo; + var isInit = !this._oldTree; + var thisStorage = this._storage; + + // Mark new root when action is treemapRootToNode. + var reRoot = (payloadType === 'treemapRootToNode' && targetInfo && thisStorage) + ? { + rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()], + direction: payload.direction + } + : null; + + var containerGroup = this._giveContainerGroup(layoutInfo); + + var renderResult = this._doRender(containerGroup, seriesModel, reRoot); + ( + !isInit && ( + !payloadType + || payloadType === 'treemapZoomToNode' + || payloadType === 'treemapRootToNode' + ) + ) + ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) + : renderResult.renderFinally(); + + this._resetController(api); + + this._renderBreadcrumb(seriesModel, api, targetInfo); + }, + + /** + * @private + */ + _giveContainerGroup: function (layoutInfo) { + var containerGroup = this._containerGroup; + if (!containerGroup) { + // FIXME + // 加一层containerGroup是为了clip,但是现在clip功能并没有实现。 + containerGroup = this._containerGroup = new Group(); + this._initEvents(containerGroup); + this.group.add(containerGroup); + } + containerGroup.attr('position', [layoutInfo.x, layoutInfo.y]); + + return containerGroup; + }, + + /** + * @private + */ + _doRender: function (containerGroup, seriesModel, reRoot) { + var thisTree = seriesModel.getData().tree; + var oldTree = this._oldTree; + + // Clear last shape records. + var lastsForAnimation = createStorage(); + var thisStorage = createStorage(); + var oldStorage = this._storage; + var willInvisibleEls = []; + var doRenderNode = zrUtil.curry( + renderNode, seriesModel, + thisStorage, oldStorage, reRoot, + lastsForAnimation, willInvisibleEls + ); + + // Notice: when thisTree and oldTree are the same tree (see list.cloneShadow), + // the oldTree is actually losted, so we can not find all of the old graphic + // elements from tree. So we use this stragegy: make element storage, move + // from old storage to new storage, clear old storage. + + dualTravel( + thisTree.root ? [thisTree.root] : [], + (oldTree && oldTree.root) ? [oldTree.root] : [], + containerGroup, + thisTree === oldTree || !oldTree, + 0 + ); + + // Process all removing. + var willDeleteEls = clearStorage(oldStorage); + + this._oldTree = thisTree; + this._storage = thisStorage; + + return { + lastsForAnimation: lastsForAnimation, + willDeleteEls: willDeleteEls, + renderFinally: renderFinally + }; + + function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) { + // When 'render' is triggered by action, + // 'this' and 'old' may be the same tree, + // we use rawIndex in that case. + if (sameTree) { + oldViewChildren = thisViewChildren; + each(thisViewChildren, function (child, index) { + !child.isRemoved() && processNode(index, index); + }); + } + // Diff hierarchically (diff only in each subtree, but not whole). + // because, consistency of view is important. + else { + (new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey)) + .add(processNode) + .update(processNode) + .remove(zrUtil.curry(processNode, null)) + .execute(); + } + + function getKey(node) { + // Identify by name or raw index. + return node.getId(); + } + + function processNode(newIndex, oldIndex) { + var thisNode = newIndex != null ? thisViewChildren[newIndex] : null; + var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null; + + var group = doRenderNode(thisNode, oldNode, parentGroup, depth); + + group && dualTravel( + thisNode && thisNode.viewChildren || [], + oldNode && oldNode.viewChildren || [], + group, + sameTree, + depth + 1 + ); + } + } + + function clearStorage(storage) { + var willDeleteEls = createStorage(); + storage && each(storage, function (store, storageName) { + var delEls = willDeleteEls[storageName]; + each(store, function (el) { + el && (delEls.push(el), el.__tmWillDelete = 1); + }); + }); + return willDeleteEls; + } + + function renderFinally() { + each(willDeleteEls, function (els) { + each(els, function (el) { + el.parent && el.parent.remove(el); + }); + }); + each(willInvisibleEls, function (el) { + el.invisible = true; + // Setting invisible is for optimizing, so no need to set dirty, + // just mark as invisible. + el.dirty(); + }); + } + }, + + /** + * @private + */ + _doAnimation: function (containerGroup, renderResult, seriesModel, reRoot) { + if (!seriesModel.get('animation')) { + return; + } + + var duration = seriesModel.get('animationDurationUpdate'); + var easing = seriesModel.get('animationEasing'); + var animationWrap = animationUtil.createWrap(); + + // Make delete animations. + each(renderResult.willDeleteEls, function (store, storageName) { + each(store, function (el, rawIndex) { + if (el.invisible) { + return; + } + + var parent = el.parent; // Always has parent, and parent is nodeGroup. + var target; + + if (reRoot && reRoot.direction === 'drillDown') { + target = parent === reRoot.rootNodeGroup + // This is the content element of view root. + // Only `content` will enter this branch, because + // `background` and `nodeGroup` will not be deleted. + ? { + shape: { + x: 0, + y: 0, + width: parent.__tmNodeWidth, + height: parent.__tmNodeHeight + }, + style: { + opacity: 0 + } + } + // Others. + : {style: {opacity: 0}}; + } + else { + var targetX = 0; + var targetY = 0; + + if (!parent.__tmWillDelete) { + // Let node animate to right-bottom corner, cooperating with fadeout, + // which is appropriate for user understanding. + // Divided by 2 for reRoot rolling up effect. + targetX = parent.__tmNodeWidth / 2; + targetY = parent.__tmNodeHeight / 2; + } + + target = storageName === 'nodeGroup' + ? {position: [targetX, targetY], style: {opacity: 0}} + : { + shape: {x: targetX, y: targetY, width: 0, height: 0}, + style: {opacity: 0} + }; + } + + target && animationWrap.add(el, target, duration, easing); + }); + }); + + // Make other animations + each(this._storage, function (store, storageName) { + each(store, function (el, rawIndex) { + var last = renderResult.lastsForAnimation[storageName][rawIndex]; + var target = {}; + + if (!last) { + return; + } + + if (storageName === 'nodeGroup') { + if (last.old) { + target.position = el.position.slice(); + el.attr('position', last.old); + } + } + else { + if (last.old) { + target.shape = zrUtil.extend({}, el.shape); + el.setShape(last.old); + } + + if (last.fadein) { + el.setStyle('opacity', 0); + target.style = {opacity: 1}; + } + // When animation is stopped for succedent animation starting, + // el.style.opacity might not be 1 + else if (el.style.opacity !== 1) { + target.style = {opacity: 1}; + } + } + + animationWrap.add(el, target, duration, easing); + }); + }, this); + + this._state = 'animating'; + + animationWrap + .done(bind(function () { + this._state = 'ready'; + renderResult.renderFinally(); + }, this)) + .start(); + }, + + /** + * @private + */ + _resetController: function (api) { + var controller = this._controller; + + // Init controller. + if (!controller) { + controller = this._controller = new RoamController(api.getZr()); + controller.enable(this.seriesModel.get('roam')); + controller.on('pan', bind(this._onPan, this)); + controller.on('zoom', bind(this._onZoom, this)); + } + + var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight()); + controller.rectProvider = function () { + return rect; + }; + }, + + /** + * @private + */ + _clearController: function () { + var controller = this._controller; + if (controller) { + controller.off('pan').off('zoom'); + controller = null; + } + }, + + /** + * @private + */ + _onPan: function (dx, dy) { + this._mayClick = false; + + if (this._state !== 'animating' + && (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) + ) { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + + if (!root) { + return; + } + + var rootLayout = root.getLayout(); + + if (!rootLayout) { + return; + } + + this.api.dispatchAction({ + type: 'treemapMove', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rootLayout.x + dx, y: rootLayout.y + dy, + width: rootLayout.width, height: rootLayout.height + } + }); + } + }, + + /** + * @private + */ + _onZoom: function (scale, mouseX, mouseY) { + this._mayClick = false; + + if (this._state !== 'animating') { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + + if (!root) { + return; + } + + var rootLayout = root.getLayout(); + + if (!rootLayout) { + return; + } + + var rect = new BoundingRect( + rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height + ); + var layoutInfo = this.seriesModel.layoutInfo; + + // Transform mouse coord from global to containerGroup. + mouseX -= layoutInfo.x; + mouseY -= layoutInfo.y; + + // Scale root bounding rect. + var m = matrix.create(); + matrix.translate(m, m, [-mouseX, -mouseY]); + matrix.scale(m, m, [scale, scale]); + matrix.translate(m, m, [mouseX, mouseY]); + + rect.applyTransform(m); + + this.api.dispatchAction({ + type: 'treemapRender', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rect.x, y: rect.y, + width: rect.width, height: rect.height + } + }); + } + }, + + /** + * @private + */ + _initEvents: function (containerGroup) { + // FIXME + // 不用click以及silent的原因是,animate时视图设置silent true来避免click生效, + // 但是animate中,按下鼠标,animate结束后(silent设回为false)松开鼠标, + // 还是会触发click,期望是不触发。 + + // Mousedown occurs when drag start, and mouseup occurs when drag end, + // click event should not be triggered in that case. + + containerGroup.on('mousedown', function (e) { + this._state === 'ready' && (this._mayClick = true); + }, this); + containerGroup.on('mouseup', function (e) { + if (this._mayClick) { + this._mayClick = false; + this._state === 'ready' && onClick.call(this, e); + } + }, this); + + function onClick(e) { + var nodeClick = this.seriesModel.get('nodeClick', true); + + if (!nodeClick) { + return; + } + + var targetInfo = this.findTarget(e.offsetX, e.offsetY); + + if (!targetInfo) { + return; + } + + var node = targetInfo.node; + if (node.getLayout().isLeafRoot) { + this._rootToNode(targetInfo); + } + else { + if (nodeClick === 'zoomToNode') { + this._zoomToNode(targetInfo); + } + else if (nodeClick === 'link') { + var itemModel = node.hostTree.data.getItemModel(node.dataIndex); + var link = itemModel.get('link', true); + var linkTarget = itemModel.get('target', true) || 'blank'; + link && window.open(link, linkTarget); + } + } + } + }, + + /** + * @private + */ + _renderBreadcrumb: function (seriesModel, api, targetInfo) { + if (!targetInfo) { + // Find breadcrumb tail on center of containerGroup. + targetInfo = this.findTarget(api.getWidth() / 2, api.getHeight() / 2); + + if (!targetInfo) { + targetInfo = {node: seriesModel.getData().tree.root}; + } + } + + (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group, bind(onSelect, this)))) + .render(seriesModel, api, targetInfo.node); + + function onSelect(node) { + if (this._state !== 'animating') { + helper.aboveViewRoot(seriesModel.getViewRoot(), node) + ? this._rootToNode({node: node}) + : this._zoomToNode({node: node}); + } + } + }, + + /** + * @override + */ + remove: function () { + this._clearController(); + this._containerGroup && this._containerGroup.removeAll(); + this._storage = createStorage(); + this._state = 'ready'; + this._breadcrumb && this._breadcrumb.remove(); + }, + + dispose: function () { + this._clearController(); + }, + + /** + * @private + */ + _zoomToNode: function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapZoomToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }, + + /** + * @private + */ + _rootToNode: function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapRootToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }, + + /** + * @public + * @param {number} x Global coord x. + * @param {number} y Global coord y. + * @return {Object} info If not found, return undefined; + * @return {number} info.node Target node. + * @return {number} info.offsetX x refer to target node. + * @return {number} info.offsetY y refer to target node. + */ + findTarget: function (x, y) { + var targetInfo; + var viewRoot = this.seriesModel.getViewRoot(); + + viewRoot.eachNode({attr: 'viewChildren', order: 'preorder'}, function (node) { + var bgEl = this._storage.background[node.getRawIndex()]; + // If invisible, there might be no element. + if (bgEl) { + var point = bgEl.transformCoordToLocal(x, y); + var shape = bgEl.shape; + + // For performance consideration, dont use 'getBoundingRect'. + if (shape.x <= point[0] + && point[0] <= shape.x + shape.width + && shape.y <= point[1] + && point[1] <= shape.y + shape.height + ) { + targetInfo = {node: node, offsetX: point[0], offsetY: point[1]}; + } + else { + return false; // Suppress visit subtree. + } + } + }, this); + + return targetInfo; + } + + }); + + /** + * @inner + */ + function createStorage() { + return {nodeGroup: [], background: [], content: []}; + } + + /** + * @inner + * @return Return undefined means do not travel further. + */ + function renderNode( + seriesModel, thisStorage, oldStorage, reRoot, + lastsForAnimation, willInvisibleEls, + thisNode, oldNode, parentGroup, depth + ) { + // Whether under viewRoot. + if (!thisNode) { + // Deleting nodes will be performed finally. This method just find + // element from old storage, or create new element, set them to new + // storage, and set styles. + return; + } + + var thisLayout = thisNode.getLayout(); + + if (!thisLayout || !thisLayout.isInView) { + return; + } + + var thisWidth = thisLayout.width; + var thisHeight = thisLayout.height; + var thisInvisible = thisLayout.invisible; + + var thisRawIndex = thisNode.getRawIndex(); + var oldRawIndex = oldNode && oldNode.getRawIndex(); + + // Node group + var group = giveGraphic('nodeGroup', Group); + + if (!group) { + return; + } + + parentGroup.add(group); + // x,y are not set when el is above view root. + group.attr('position', [thisLayout.x || 0, thisLayout.y || 0]); + group.__tmNodeWidth = thisWidth; + group.__tmNodeHeight = thisHeight; + + if (thisLayout.isAboveViewRoot) { + return group; + } + + // Background + var bg = giveGraphic('background', Rect, depth, Z_BG); + if (bg) { + bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight}); + updateStyle(bg, function () { + bg.setStyle('fill', thisNode.getVisual('borderColor', true)); + }); + group.add(bg); + } + + var thisViewChildren = thisNode.viewChildren; + + // No children, render content. + if (!thisViewChildren || !thisViewChildren.length) { + var content = giveGraphic('content', Rect, depth, Z_CONTENT); + content && renderContent(group); + } + + return group; + + // ---------------------------- + // | Procedures in renderNode | + // ---------------------------- + + function renderContent(group) { + // For tooltip. + content.dataIndex = thisNode.dataIndex; + content.seriesIndex = seriesModel.seriesIndex; + + var borderWidth = thisLayout.borderWidth; + var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0); + var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0); + + content.culling = true; + content.setShape({ + x: borderWidth, + y: borderWidth, + width: contentWidth, + height: contentHeight + }); + + var visualColor = thisNode.getVisual('color', true); + updateStyle(content, function () { + var normalStyle = {fill: visualColor}; + var emphasisStyle = thisNode.getModel('itemStyle.emphasis').getItemStyle(); + + prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight); + + content.setStyle(normalStyle); + graphic.setHoverStyle(content, emphasisStyle); + }); + + group.add(content); + } + + function updateStyle(element, cb) { + if (!thisInvisible) { + // If invisible, do not set visual, otherwise the element will + // change immediately before animation. We think it is OK to + // remain its origin color when moving out of the view window. + cb(); + + if (!element.__tmWillVisible) { + element.invisible = false; + } + } + else { + // Delay invisible setting utill animation finished, + // avoid element vanish suddenly before animation. + !element.invisible && willInvisibleEls.push(element); + } + } + + function prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight) { + var nodeModel = thisNode.getModel(); + var text = nodeModel.get('name'); + if (thisLayout.isLeafRoot) { + var iconChar = seriesModel.get('drillDownIcon', true); + text = iconChar ? iconChar + ' ' + text : test; + } + + setText( + text, normalStyle, nodeModel, PATH_LABEL_NORMAL, + visualColor, contentWidth, contentHeight + ); + setText( + text, emphasisStyle, nodeModel, PATH_LABEL_EMPHASIS, + visualColor, contentWidth, contentHeight + ); + } + + function setText(text, style, nodeModel, labelPath, visualColor, contentWidth, contentHeight) { + var labelModel = nodeModel.getModel(labelPath); + var labelTextStyleModel = labelModel.getModel('textStyle'); + + graphic.setText(style, labelModel, visualColor); + + // text.align and text.baseline is not included by graphic.setText, + // because in most cases the two attributes are not exposed to user, + // except in treemap. + style.textAlign = labelTextStyleModel.get('align'); + style.textVerticalAlign = labelTextStyleModel.get('baseline'); + + var textRect = labelTextStyleModel.getTextRect(text); + if (!labelModel.getShallow('show') || textRect.height > contentHeight) { + style.text = ''; + } + else if (textRect.width > contentWidth) { + style.text = labelTextStyleModel.get('ellipsis') + ? labelTextStyleModel.truncateText( + text, contentWidth, null, {minChar: 2} + ) + : ''; + } + else { + style.text = text; + } + } + + function giveGraphic(storageName, Ctor, depth, z) { + var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex]; + var lasts = lastsForAnimation[storageName]; + + if (element) { + // Remove from oldStorage + oldStorage[storageName][oldRawIndex] = null; + prepareAnimationWhenHasOld(lasts, element, storageName); + } + // If invisible and no old element, do not create new element (for optimizing). + else if (!thisInvisible) { + element = new Ctor({z: calculateZ(depth, z)}); + element.__tmDepth = depth; + element.__tmStorageName = storageName; + prepareAnimationWhenNoOld(lasts, element, storageName); + } + + // Set to thisStorage + return (thisStorage[storageName][thisRawIndex] = element); + } + + function prepareAnimationWhenHasOld(lasts, element, storageName) { + var lastCfg = lasts[thisRawIndex] = {}; + lastCfg.old = storageName === 'nodeGroup' + ? element.position.slice() + : zrUtil.extend({}, element.shape); + } + + // If a element is new, we need to find the animation start point carefully, + // otherwise it will looks strange when 'zoomToNode'. + function prepareAnimationWhenNoOld(lasts, element, storageName) { + var lastCfg = lasts[thisRawIndex] = {}; + var parentNode = thisNode.parentNode; + + if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) { + var parentOldX = 0; + var parentOldY = 0; + + // New nodes appear from right-bottom corner in 'zoomToNode' animation. + // For convenience, get old bounding rect from background. + var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()]; + if (!reRoot && parentOldBg && parentOldBg.old) { + parentOldX = parentOldBg.old.width; + parentOldY = parentOldBg.old.height; + } + + // When no parent old shape found, its parent is new too, + // so we can just use {x:0, y:0}. + lastCfg.old = storageName === 'nodeGroup' + ? [0, parentOldY] + : {x: parentOldX, y: parentOldY, width: 0, height: 0}; + } + + // Fade in, user can be aware that these nodes are new. + lastCfg.fadein = storageName !== 'nodeGroup'; + } + } + + // We can not set all backgroud with the same z, Because the behaviour of + // drill down and roll up differ background creation sequence from tree + // hierarchy sequence, which cause that lowser background element overlap + // upper ones. So we calculate z based on depth. + // Moreover, we try to shrink down z interval to [0, 1] to avoid that + // treemap with large z overlaps other components. + function calculateZ(depth, zInLevel) { + var zb = depth * Z_BASE + zInLevel; + return (zb - 1) / zb; + } + + + +/***/ }, +/* 187 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var helper = { + + retrieveTargetInfo: function (payload, seriesModel) { + if (payload + && ( + payload.type === 'treemapZoomToNode' + || payload.type === 'treemapRootToNode' + ) + ) { + var root = seriesModel.getData().tree.root; + var targetNode = payload.targetNode; + if (targetNode && root.contains(targetNode)) { + return {node: targetNode}; + } + + var targetNodeId = payload.targetNodeId; + if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) { + return {node: targetNode}; + } + } + }, + + // Not includes the given node at the last item. + getPathToRoot: function (node) { + var path = []; + while (node) { + node = node.parentNode; + node && path.push(node); + } + return path.reverse(); + }, + + aboveViewRoot: function (viewRoot, node) { + var viewPath = helper.getPathToRoot(viewRoot); + return zrUtil.indexOf(viewPath, node) >= 0; + } + }; + + module.exports = helper; + + +/***/ }, +/* 188 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + var zrUtil = __webpack_require__(4); + + var TEXT_PADDING = 8; + var ITEM_GAP = 8; + var ARRAY_LENGTH = 5; + + function Breadcrumb(containerGroup, onSelect) { + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group = new graphic.Group(); + + containerGroup.add(this.group); + + /** + * @private + * @type {Function} + */ + this._onSelect = onSelect || zrUtil.noop; + } + + Breadcrumb.prototype = { + + constructor: Breadcrumb, + + render: function (seriesModel, api, targetNode) { + var model = seriesModel.getModel('breadcrumb'); + var thisGroup = this.group; + + thisGroup.removeAll(); + + if (!model.get('show') || !targetNode) { + return; + } + + var normalStyleModel = model.getModel('itemStyle.normal'); + // var emphasisStyleModel = model.getModel('itemStyle.emphasis'); + var textStyleModel = normalStyleModel.getModel('textStyle'); + + var layoutParam = { + pos: { + left: model.get('left'), + right: model.get('right'), + top: model.get('top'), + bottom: model.get('bottom') + }, + box: { + width: api.getWidth(), + height: api.getHeight() + }, + emptyItemWidth: model.get('emptyItemWidth'), + totalWidth: 0, + renderList: [] + }; + + this._prepare( + model, targetNode, layoutParam, textStyleModel + ); + this._renderContent( + model, targetNode, layoutParam, normalStyleModel, textStyleModel + ); + + layout.positionGroup(thisGroup, layoutParam.pos, layoutParam.box); + }, + + /** + * Prepare render list and total width + * @private + */ + _prepare: function (model, targetNode, layoutParam, textStyleModel) { + for (var node = targetNode; node; node = node.parentNode) { + var text = node.getModel().get('name'); + var textRect = textStyleModel.getTextRect(text); + var itemWidth = Math.max( + textRect.width + TEXT_PADDING * 2, + layoutParam.emptyItemWidth + ); + layoutParam.totalWidth += itemWidth + ITEM_GAP; + layoutParam.renderList.push({node: node, text: text, width: itemWidth}); + } + }, + + /** + * @private + */ + _renderContent: function ( + model, targetNode, layoutParam, normalStyleModel, textStyleModel + ) { + // Start rendering. + var lastX = 0; + var emptyItemWidth = layoutParam.emptyItemWidth; + var height = model.get('height'); + var availableSize = layout.getAvailableSize(layoutParam.pos, layoutParam.box); + var totalWidth = layoutParam.totalWidth; + var renderList = layoutParam.renderList; + + for (var i = renderList.length - 1; i >= 0; i--) { + var item = renderList[i]; + var itemWidth = item.width; + var text = item.text; + + // Hdie text and shorten width if necessary. + if (totalWidth > availableSize.width) { + totalWidth -= itemWidth - emptyItemWidth; + itemWidth = emptyItemWidth; + text = ''; + } + + this.group.add(new graphic.Polygon({ + shape: { + points: makeItemPoints( + lastX, 0, itemWidth, height, + i === renderList.length - 1, i === 0 + ) + }, + style: zrUtil.defaults( + normalStyleModel.getItemStyle(), + { + lineJoin: 'bevel', + text: text, + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + } + ), + z: 10, + onclick: zrUtil.bind(this._onSelect, this, item.node) + })); + + lastX += itemWidth + ITEM_GAP; + } + }, + + /** + * @override + */ + remove: function () { + this.group.removeAll(); + } + }; + + function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) { + var points = [ + [head ? x : x - ARRAY_LENGTH, y], + [x + itemWidth, y], + [x + itemWidth, y + itemHeight], + [head ? x : x - ARRAY_LENGTH, y + itemHeight] + ]; + !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]); + !head && points.push([x, y + itemHeight / 2]); + return points; + } + + module.exports = Breadcrumb; + + +/***/ }, +/* 189 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + /** + * @param {number} [time=500] Time in ms + * @param {string} [easing='linear'] + * @param {number} [delay=0] + * @param {Function} [callback] + * + * @example + * // Animate position + * animation + * .createWrap() + * .add(el1, {position: [10, 10]}) + * .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400) + * .done(function () { // done }) + * .start('cubicOut'); + */ + function createWrap() { + + var storage = []; + var elExistsMap = {}; + var doneCallback; + + return { + + /** + * Caution: a el can only be added once, otherwise 'done' + * might not be called. This method checks this (by el.id), + * suppresses adding and returns false when existing el found. + * + * @param {modele:zrender/Element} el + * @param {Object} target + * @param {number} [time=500] + * @param {number} [delay=0] + * @param {string} [easing='linear'] + * @return {boolean} Whether adding succeeded. + * + * @example + * add(el, target, time, delay, easing); + * add(el, target, time, easing); + * add(el, target, time); + * add(el, target); + */ + add: function (el, target, time, delay, easing) { + if (zrUtil.isString(delay)) { + easing = delay; + delay = 0; + } + + if (elExistsMap[el.id]) { + return false; + } + elExistsMap[el.id] = 1; + + storage.push( + {el: el, target: target, time: time, delay: delay, easing: easing} + ); + + return true; + }, + + /** + * Only execute when animation finished. Will not execute when any + * of 'stop' or 'stopAnimation' called. + * + * @param {Function} callback + */ + done: function (callback) { + doneCallback = callback; + return this; + }, + + /** + * Will stop exist animation firstly. + */ + start: function () { + var count = storage.length; + + for (var i = 0, len = storage.length; i < len; i++) { + var item = storage[i]; + item.el.animateTo(item.target, item.time, item.delay, item.easing, done); + } + + return this; + + function done() { + count--; + if (!count) { + storage.length = 0; + elExistsMap = {}; + doneCallback && doneCallback(); + } + } + } + }; + } + + module.exports = {createWrap: createWrap}; + + +/***/ }, +/* 190 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Treemap action + */ + + + var echarts = __webpack_require__(1); + var helper = __webpack_require__(187); + + var noop = function () {}; + + var actionTypes = [ + 'treemapZoomToNode', + 'treemapRender', + 'treemapMove' + ]; + + for (var i = 0; i < actionTypes.length; i++) { + echarts.registerAction({type: actionTypes[i], update: 'updateView'}, noop); + } + + echarts.registerAction( + {type: 'treemapRootToNode', update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'treemap', query: payload}, + handleRootToNode + ); + + function handleRootToNode(model, index) { + var targetInfo = helper.retrieveTargetInfo(payload, model); + + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = helper.aboveViewRoot(originViewRoot, targetInfo.node) + ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + } + ); + + + +/***/ }, +/* 191 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapping = __webpack_require__(192); + var zrColor = __webpack_require__(39); + var zrUtil = __webpack_require__(4); + var isArray = zrUtil.isArray; + + var ITEM_STYLE_NORMAL = 'itemStyle.normal'; + + module.exports = function (ecModel, api, payload) { + + var condition = {mainType: 'series', subType: 'treemap', query: payload}; + ecModel.eachComponent(condition, function (seriesModel) { + + var tree = seriesModel.getData().tree; + var root = tree.root; + var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL); + + if (root.isRemoved()) { + return; + } + + var levelItemStyles = zrUtil.map(tree.levelModels, function (levelModel) { + return levelModel ? levelModel.get(ITEM_STYLE_NORMAL) : null; + }); + + travelTree( + root, // Visual should calculate from tree root but not view root. + {}, + levelItemStyles, + seriesItemStyleModel, + seriesModel.getViewRoot().getAncestors(), + seriesModel + ); + }); + }; + + function travelTree( + node, designatedVisual, levelItemStyles, seriesItemStyleModel, + viewRootAncestors, seriesModel + ) { + var nodeModel = node.getModel(); + var nodeLayout = node.getLayout(); + + // Optimize + if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) { + return; + } + + var nodeItemStyleModel = node.getModel(ITEM_STYLE_NORMAL); + var levelItemStyle = levelItemStyles[node.depth]; + var visuals = buildVisuals( + nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel + ); + + // calculate border color + var borderColor = nodeItemStyleModel.get('borderColor'); + var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation'); + var thisNodeColor; + if (borderColorSaturation != null) { + // For performance, do not always execute 'calculateColor'. + thisNodeColor = calculateColor(visuals, node); + borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor); + } + node.setVisual('borderColor', borderColor); + + var viewChildren = node.viewChildren; + if (!viewChildren || !viewChildren.length) { + thisNodeColor = calculateColor(visuals, node); + // Apply visual to this node. + node.setVisual('color', thisNodeColor); + } + else { + var mapping = buildVisualMapping( + node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren + ); + // Designate visual to children. + zrUtil.each(viewChildren, function (child, index) { + // If higher than viewRoot, only ancestors of viewRoot is needed to visit. + if (child.depth >= viewRootAncestors.length + || child === viewRootAncestors[child.depth] + ) { + var childVisual = mapVisual( + nodeModel, visuals, child, index, mapping, seriesModel + ); + travelTree( + child, childVisual, levelItemStyles, seriesItemStyleModel, + viewRootAncestors, seriesModel + ); + } + }); + } + } + + function buildVisuals( + nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel + ) { + var visuals = zrUtil.extend({}, designatedVisual); + + zrUtil.each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) { + // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel + var val = nodeItemStyleModel.get(visualName, true); // Ignore parent + val == null && levelItemStyle && (val = levelItemStyle[visualName]); + val == null && (val = designatedVisual[visualName]); + val == null && (val = seriesItemStyleModel.get(visualName)); + + val != null && (visuals[visualName] = val); + }); + + return visuals; + } + + function calculateColor(visuals) { + var color = getValueVisualDefine(visuals, 'color'); + + if (color) { + var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha'); + var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation'); + if (colorSaturation) { + color = zrColor.modifyHSL(color, null, null, colorSaturation); + } + if (colorAlpha) { + color = zrColor.modifyAlpha(color, colorAlpha); + } + + return color; + } + } + + function calculateBorderColor(borderColorSaturation, thisNodeColor) { + return thisNodeColor != null + ? zrColor.modifyHSL(thisNodeColor, null, null, borderColorSaturation) + : null; + } + + function getValueVisualDefine(visuals, name) { + var value = visuals[name]; + if (value != null && value !== 'none') { + return value; + } + } + + function buildVisualMapping( + node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren + ) { + if (!viewChildren || !viewChildren.length) { + return; + } + + var rangeVisual = getRangeVisual(nodeModel, 'color') + || ( + visuals.color != null + && visuals.color !== 'none' + && ( + getRangeVisual(nodeModel, 'colorAlpha') + || getRangeVisual(nodeModel, 'colorSaturation') + ) + ); + + if (!rangeVisual) { + return; + } + + var colorMappingBy = nodeModel.get('colorMappingBy'); + var opt = { + type: rangeVisual.name, + dataExtent: nodeLayout.dataExtent, + visual: rangeVisual.range + }; + if (opt.type === 'color' + && (colorMappingBy === 'index' || colorMappingBy === 'id') + ) { + opt.mappingMethod = 'category'; + opt.loop = true; + // categories is ordinal, so do not set opt.categories. + } + else { + opt.mappingMethod = 'linear'; + } + + var mapping = new VisualMapping(opt); + mapping.__drColorMappingBy = colorMappingBy; + + return mapping; + } + + // Notice: If we dont have the attribute 'colorRange', but only use + // attribute 'color' to represent both concepts of 'colorRange' and 'color', + // (It means 'colorRange' when 'color' is Array, means 'color' when not array), + // this problem will be encountered: + // If a level-1 node dont have children, and its siblings has children, + // and colorRange is set on level-1, then the node can not be colored. + // So we separate 'colorRange' and 'color' to different attributes. + function getRangeVisual(nodeModel, name) { + // 'colorRange', 'colorARange', 'colorSRange'. + // If not exsits on this node, fetch from levels and series. + var range = nodeModel.get(name); + return (isArray(range) && range.length) ? {name: name, range: range} : null; + } + + function mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) { + var childVisuals = zrUtil.extend({}, visuals); + + if (mapping) { + var mappingType = mapping.type; + var colorMappingBy = mappingType === 'color' && mapping.__drColorMappingBy; + var value = + colorMappingBy === 'index' + ? index + : colorMappingBy === 'id' + ? seriesModel.mapIdToIndex(child.getId()) + : child.getValue(nodeModel.get('visualDimension')); + + childVisuals[mappingType] = mapping.mapValueToVisual(value); + } + + return childVisuals; + } + + + +/***/ }, +/* 192 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual mapping. + */ + + + var zrUtil = __webpack_require__(4); + var zrColor = __webpack_require__(39); + var linearMap = __webpack_require__(7).linearMap; + var each = zrUtil.each; + var isObject = zrUtil.isObject; + + var CATEGORY_DEFAULT_VISUAL_INDEX = -1; + + /** + * @param {Object} option + * @param {string} [option.type] See visualHandlers. + * @param {string} [option.mappingMethod] 'linear' or 'piecewise' or 'category' or 'fixed' + * @param {Array.=} [option.dataExtent] [minExtent, maxExtent], + * required when mappingMethod is 'linear' + * @param {Array.=} [option.pieceList] [ + * {value: someValue}, + * {interval: [min1, max1], visual: {...}}, + * {interval: [min2, max2]} + * ], + * required when mappingMethod is 'piecewise'. + * Visual for only each piece can be specified. + * @param {Array.=} [option.categories] ['cate1', 'cate2'] + * required when mappingMethod is 'category'. + * If no option.categories, categories is set + * as [0, 1, 2, ...]. + * @param {boolean} [option.loop=false] Whether loop mapping when mappingMethod is 'category'. + * @param {(Array|Object|*)} [option.visual] Visual data. + * when mappingMethod is 'category', + * visual data can be array or object + * (like: {cate1: '#222', none: '#fff'}) + * or primary types (which represents + * defualt category visual), otherwise visual + * can be array or primary (which will be + * normalized to array). + * + */ + var VisualMapping = function (option) { + var mappingMethod = option.mappingMethod; + var visualType = option.type; + + /** + * @readOnly + * @type {Object} + */ + var thisOption = this.option = zrUtil.clone(option); + + /** + * @readOnly + * @type {string} + */ + this.type = visualType; + + /** + * @readOnly + * @type {string} + */ + this.mappingMethod = mappingMethod; + + /** + * @private + * @type {Function} + */ + this._normalizeData = normalizers[mappingMethod]; + + var visualHandler = visualHandlers[visualType]; + + /** + * @public + * @type {Function} + */ + this.applyVisual = visualHandler.applyVisual; + + /** + * @public + * @type {Function} + */ + this.getColorMapper = visualHandler.getColorMapper; + + /** + * @private + * @type {Function} + */ + this._doMap = visualHandler._doMap[mappingMethod]; + + if (mappingMethod === 'piecewise') { + normalizeVisualRange(thisOption); + preprocessForPiecewise(thisOption); + } + else if (mappingMethod === 'category') { + thisOption.categories + ? preprocessForSpecifiedCategory(thisOption) + // categories is ordinal when thisOption.categories not specified, + // which need no more preprocess except normalize visual. + : normalizeVisualRange(thisOption, true); + } + else { // mappingMethod === 'linear' or 'fixed' + zrUtil.assert(mappingMethod !== 'linear' || thisOption.dataExtent); + normalizeVisualRange(thisOption); + } + }; + + VisualMapping.prototype = { + + constructor: VisualMapping, + + mapValueToVisual: function (value) { + var normalized = this._normalizeData(value); + return this._doMap(normalized, value); + }, + + getNormalizer: function () { + return zrUtil.bind(this._normalizeData, this); + } + }; + + var visualHandlers = VisualMapping.visualHandlers = { + + color: { + + applyVisual: makeApplyVisual('color'), + + /** + * Create a mapper function + * @return {Function} + */ + getColorMapper: function () { + var thisOption = this.option; + var parsedVisual = zrUtil.map(thisOption.visual, zrColor.parse); + + return zrUtil.bind( + thisOption.mappingMethod === 'category' + ? function (value, isNormalized) { + !isNormalized && (value = this._normalizeData(value)); + return doMapCategory(this, value); + } + : function (value, isNormalized, out) { + // If output rgb array + // which will be much faster and useful in pixel manipulation + var returnRGBArray = !!out; + !isNormalized && (value = this._normalizeData(value)); + out = zrColor.fastMapToColor(value, parsedVisual, out); + return returnRGBArray ? out : zrUtil.stringify(out, 'rgba'); + }, + this + ); + }, + + _doMap: { + linear: function (normalized) { + return zrColor.mapToColor(normalized, this.option.visual); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = zrColor.mapToColor(normalized, this.option.visual); + } + return result; + }, + fixed: doMapFixed + } + }, + + colorHue: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, value); + }), + + colorSaturation: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, null, value); + }), + + colorLightness: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, null, null, value); + }), + + colorAlpha: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyAlpha(color, value); + }), + + opacity: { + applyVisual: makeApplyVisual('opacity'), + _doMap: makeDoMap([0, 1]) + }, + + symbol: { + applyVisual: function (value, getter, setter) { + var symbolCfg = this.mapValueToVisual(value); + if (zrUtil.isString(symbolCfg)) { + setter('symbol', symbolCfg); + } + else if (isObject(symbolCfg)) { + for (var name in symbolCfg) { + if (symbolCfg.hasOwnProperty(name)) { + setter(name, symbolCfg[name]); + } + } + } + }, + _doMap: { + linear: doMapToArray, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = doMapToArray.call(this, normalized); + } + return result; + }, + fixed: doMapFixed + } + }, + + symbolSize: { + applyVisual: makeApplyVisual('symbolSize'), + _doMap: makeDoMap([0, 1]) + } + }; + + + function preprocessForPiecewise(thisOption) { + var pieceList = thisOption.pieceList; + thisOption.hasSpecialVisual = false; + + zrUtil.each(pieceList, function (piece, index) { + piece.originIndex = index; + // piece.visual is "result visual value" but not + // a visual range, so it does not need to be normalized. + if (piece.visual != null) { + thisOption.hasSpecialVisual = true; + } + }); + } + + function preprocessForSpecifiedCategory(thisOption) { + // Hash categories. + var categories = thisOption.categories; + var visual = thisOption.visual; + + var categoryMap = thisOption.categoryMap = {}; + each(categories, function (cate, index) { + categoryMap[cate] = index; + }); + + // Process visual map input. + if (!zrUtil.isArray(visual)) { + var visualArr = []; + + if (zrUtil.isObject(visual)) { + each(visual, function (v, cate) { + var index = categoryMap[cate]; + visualArr[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v; + }); + } + else { // Is primary type, represents default visual. + visualArr[CATEGORY_DEFAULT_VISUAL_INDEX] = visual; + } + + visual = thisOption.visual = visualArr; + } + + // Remove categories that has no visual, + // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX. + for (var i = categories.length - 1; i >= 0; i--) { + if (visual[i] == null) { + delete categoryMap[categories[i]]; + categories.pop(); + } + } + } + + function normalizeVisualRange(thisOption, isCategory) { + var visual = thisOption.visual; + var visualArr = []; + + if (zrUtil.isObject(visual)) { + each(visual, function (v) { + visualArr.push(v); + }); + } + else if (visual != null) { + visualArr.push(visual); + } + + var doNotNeedPair = {color: 1, symbol: 1}; + + if (!isCategory + && visualArr.length === 1 + && !(thisOption.type in doNotNeedPair) + ) { + // Do not care visualArr.length === 0, which is illegal. + visualArr[1] = visualArr[0]; + } + + thisOption.visual = visualArr; + } + + function makePartialColorVisualHandler(applyValue) { + return { + applyVisual: function (value, getter, setter) { + value = this.mapValueToVisual(value); + // Must not be array value + setter('color', applyValue(getter('color'), value)); + }, + _doMap: makeDoMap([0, 1]) + }; + } + + function doMapToArray(arr, normalized) { + var visual = this.option.visual; + return visual[ + Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true)) + ] || {}; + } + + function makeApplyVisual(visualType) { + return function (value, getter, setter) { + setter(visualType, this.mapValueToVisual(value)); + }; + } + + function doMapCategory(normalized) { + var visual = this.option.visual; + return visual[ + (this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX) + ? normalized % visual.length + : normalized + ]; + } + + function doMapFixed() { + return this.option.visual[0]; + } + + function makeDoMap(sourceExtent) { + return { + linear: function (normalized) { + return linearMap(normalized, sourceExtent, this.option.visual, true); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = linearMap(normalized, sourceExtent, this.option.visual, true); + } + return result; + }, + fixed: doMapFixed + }; + } + + function getSpecifiedVisual(value) { + var thisOption = this.option; + var pieceList = thisOption.pieceList; + if (thisOption.hasSpecialVisual) { + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList); + var piece = pieceList[pieceIndex]; + if (piece && piece.visual) { + return piece.visual[this.type]; + } + } + } + + + /** + * Normalizers by mapping methods. + */ + var normalizers = { + + linear: function (value) { + return linearMap(value, this.option.dataExtent, [0, 1], true); + }, + + piecewise: function (value) { + var pieceList = this.option.pieceList; + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true); + if (pieceIndex != null) { + return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true); + } + }, + + category: function (value) { + var index = this.option.categories + ? this.option.categoryMap[value] + : value; // ordinal + return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index; + }, + + fixed: zrUtil.noop + }; + + + + /** + * @public + */ + VisualMapping.addVisualHandler = function (name, handler) { + visualHandlers[name] = handler; + }; + + /** + * @public + */ + VisualMapping.isValidType = function (visualType) { + return visualHandlers.hasOwnProperty(visualType); + }; + + /** + * Convinent method. + * Visual can be Object or Array or primary type. + * + * @public + */ + VisualMapping.eachVisual = function (visual, callback, context) { + if (zrUtil.isObject(visual)) { + zrUtil.each(visual, callback, context); + } + else { + callback.call(context, visual); + } + }; + + VisualMapping.mapVisual = function (visual, callback, context) { + var isPrimary; + var newVisual = zrUtil.isArray(visual) + ? [] + : zrUtil.isObject(visual) + ? {} + : (isPrimary = true, null); + + VisualMapping.eachVisual(visual, function (v, key) { + var newVal = callback.call(context, v, key); + isPrimary ? (newVisual = newVal) : (newVisual[key] = newVal); + }); + return newVisual; + }; + + /** + * @public + * @param {Object} obj + * @return {Oject} new object containers visual values. + * If no visuals, return null. + */ + VisualMapping.retrieveVisuals = function (obj) { + var ret = {}; + var hasVisual; + + obj && each(visualHandlers, function (h, visualType) { + if (obj.hasOwnProperty(visualType)) { + ret[visualType] = obj[visualType]; + hasVisual = true; + } + }); + + return hasVisual ? ret : null; + }; + + /** + * Give order to visual types, considering colorSaturation, colorAlpha depends on color. + * + * @public + * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...} + * IF Array, like: ['color', 'symbol', 'colorSaturation'] + * @return {Array.} Sorted visual types. + */ + VisualMapping.prepareVisualTypes = function (visualTypes) { + if (isObject(visualTypes)) { + var types = []; + each(visualTypes, function (item, type) { + types.push(type); + }); + visualTypes = types; + } + else if (zrUtil.isArray(visualTypes)) { + visualTypes = visualTypes.slice(); + } + else { + return []; + } + + visualTypes.sort(function (type1, type2) { + // color should be front of colorSaturation, colorAlpha, ... + // symbol and symbolSize do not matter. + return (type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0) + ? 1 : -1; + }); + + return visualTypes; + }; + + /** + * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'. + * Other visuals are only depends on themself. + * + * @public + * @param {string} visualType1 + * @param {string} visualType2 + * @return {boolean} + */ + VisualMapping.dependsOn = function (visualType1, visualType2) { + return visualType2 === 'color' + ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) + : visualType1 === visualType2; + }; + + /** + * @param {number} value + * @param {Array.} pieceList [{value: ..., interval: [min, max]}, ...] + * Always from small to big. + * @param {boolean} [findClosestWhenOutside=false] + * @return {number} index + */ + VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) { + var possibleI; + var abs = Infinity; + + // value has the higher priority. + for (var i = 0, len = pieceList.length; i < len; i++) { + var pieceValue = pieceList[i].value; + if (pieceValue != null) { + if (pieceValue === value) { + return i; + } + findClosestWhenOutside && updatePossible(pieceValue, i); + } + } + + for (var i = 0, len = pieceList.length; i < len; i++) { + var piece = pieceList[i]; + var interval = piece.interval; + var close = piece.close; + + if (interval) { + if (interval[0] === -Infinity) { + if (littleThan(close[1], value, interval[1])) { + return i; + } + } + else if (interval[1] === Infinity) { + if (littleThan(close[0], interval[0], value)) { + return i; + } + } + else if ( + littleThan(close[0], interval[0], value) + && littleThan(close[1], value, interval[1]) + ) { + return i; + } + findClosestWhenOutside && updatePossible(interval[0], i); + findClosestWhenOutside && updatePossible(interval[1], i); + } + } + + if (findClosestWhenOutside) { + return value === Infinity + ? pieceList.length - 1 + : value === -Infinity + ? 0 + : possibleI; + } + + function updatePossible(val, index) { + var newAbs = Math.abs(val - value); + if (newAbs < abs) { + abs = newAbs; + possibleI = index; + } + } + + }; + + function littleThan(close, a, b) { + return close ? a <= b : a < b; + } + + module.exports = VisualMapping; + + + +/***/ }, +/* 193 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var layout = __webpack_require__(21); + var helper = __webpack_require__(187); + var BoundingRect = __webpack_require__(9); + var helper = __webpack_require__(187); + + var mathMax = Math.max; + var mathMin = Math.min; + var parsePercent = numberUtil.parsePercent; + var retrieveValue = zrUtil.retrieve; + var each = zrUtil.each; + + /** + * @public + */ + function update(ecModel, api, payload) { + // Layout result in each node: + // {x, y, width, height, area, borderWidth} + var condition = {mainType: 'series', subType: 'treemap', query: payload}; + ecModel.eachComponent(condition, function (seriesModel) { + + var ecWidth = api.getWidth(); + var ecHeight = api.getHeight(); + var seriesOption = seriesModel.option; + + var size = seriesOption.size || []; // Compatible with ec2. + var containerWidth = parsePercent( + retrieveValue(seriesOption.width, size[0]), + ecWidth + ); + var containerHeight = parsePercent( + retrieveValue(seriesOption.height, size[1]), + ecHeight + ); + + var layoutInfo = layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + // Fetch payload info. + var payloadType = payload && payload.type; + var targetInfo = helper.retrieveTargetInfo(payload, seriesModel); + var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove') + ? payload.rootRect : null; + var viewRoot = seriesModel.getViewRoot(); + var viewAbovePath = helper.getPathToRoot(viewRoot); + + if (payloadType !== 'treemapMove') { + var rootSize = payloadType === 'treemapZoomToNode' + ? estimateRootSize( + seriesModel, targetInfo, viewRoot, containerWidth, containerHeight + ) + : rootRect + ? [rootRect.width, rootRect.height] + : [containerWidth, containerHeight]; + + var sort = seriesOption.sort; + if (sort && sort !== 'asc' && sort !== 'desc') { + sort = 'desc'; + } + var options = { + squareRatio: seriesOption.squareRatio, + sort: sort, + leafDepth: seriesOption.leafDepth + }; + + // layout should be cleared because using updateView but not update. + viewRoot.hostTree.clearLayouts(); + + // TODO + // optimize: if out of view clip, do not layout. + // But take care that if do not render node out of view clip, + // how to calculate start po + + var viewRootLayout = { + x: 0, y: 0, + width: rootSize[0], height: rootSize[1], + area: rootSize[0] * rootSize[1] + }; + viewRoot.setLayout(viewRootLayout); + + squarify(viewRoot, options, false, 0); + // Supplement layout. + var viewRootLayout = viewRoot.getLayout(); + each(viewAbovePath, function (node, index) { + var childValue = (viewAbovePath[index + 1] || viewRoot).getValue(); + node.setLayout(zrUtil.extend( + {dataExtent: [childValue, childValue], borderWidth: 0}, + viewRootLayout + )); + }); + } + + var treeRoot = seriesModel.getData().tree.root; + + treeRoot.setLayout( + calculateRootPosition(layoutInfo, rootRect, targetInfo), + true + ); + + seriesModel.setLayoutInfo(layoutInfo); + + // FIXME + // 现在没有clip功能,暂时取ec高宽。 + prunning( + treeRoot, + // Transform to base element coordinate system. + new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), + viewAbovePath, + viewRoot, + 0 + ); + }); + } + + /** + * Layout treemap with squarify algorithm. + * @see https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf + * @see https://github.com/mbostock/d3/blob/master/src/layout/treemap.js + * + * @protected + * @param {module:echarts/data/Tree~TreeNode} node + * @param {Object} options + * @param {string} options.sort 'asc' or 'desc' + * @param {number} options.squareRatio + * @param {boolean} hideChildren + * @param {number} depth + */ + function squarify(node, options, hideChildren, depth) { + var width; + var height; + + if (node.isRemoved()) { + return; + } + + var thisLayout = node.getLayout(); + width = thisLayout.width; + height = thisLayout.height; + + // Considering border and gap + var itemStyleModel = node.getModel('itemStyle.normal'); + var borderWidth = itemStyleModel.get('borderWidth'); + var halfGapWidth = itemStyleModel.get('gapWidth') / 2; + var layoutOffset = borderWidth - halfGapWidth; + var nodeModel = node.getModel(); + + node.setLayout({borderWidth: borderWidth}, true); + + width = mathMax(width - 2 * layoutOffset, 0); + height = mathMax(height - 2 * layoutOffset, 0); + + var totalArea = width * height; + var viewChildren = initChildren( + node, nodeModel, totalArea, options, hideChildren, depth + ); + + if (!viewChildren.length) { + return; + } + + var rect = {x: layoutOffset, y: layoutOffset, width: width, height: height}; + var rowFixedLength = mathMin(width, height); + var best = Infinity; // the best row score so far + var row = []; + row.area = 0; + + for (var i = 0, len = viewChildren.length; i < len;) { + var child = viewChildren[i]; + + row.push(child); + row.area += child.getLayout().area; + var score = worst(row, rowFixedLength, options.squareRatio); + + // continue with this orientation + if (score <= best) { + i++; + best = score; + } + // abort, and try a different orientation + else { + row.area -= row.pop().getLayout().area; + position(row, rowFixedLength, rect, halfGapWidth, false); + rowFixedLength = mathMin(rect.width, rect.height); + row.length = row.area = 0; + best = Infinity; + } + } + + if (row.length) { + position(row, rowFixedLength, rect, halfGapWidth, true); + } + + if (!hideChildren) { + var childrenVisibleMin = nodeModel.get('childrenVisibleMin'); + if (childrenVisibleMin != null && totalArea < childrenVisibleMin) { + hideChildren = true; + } + } + + for (var i = 0, len = viewChildren.length; i < len; i++) { + squarify(viewChildren[i], options, hideChildren, depth + 1); + } + } + + /** + * Set area to each child, and calculate data extent for visual coding. + */ + function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) { + var viewChildren = node.children || []; + var orderBy = options.sort; + orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null); + + var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; + + // leafDepth has higher priority. + if (hideChildren && !overLeafDepth) { + return (node.viewChildren = []); + } + + // Sort children, order by desc. + viewChildren = zrUtil.filter(viewChildren, function (child) { + return !child.isRemoved(); + }); + + sort(viewChildren, orderBy); + + var info = statistic(nodeModel, viewChildren, orderBy); + + if (info.sum === 0) { + return (node.viewChildren = []); + } + + info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren); + + if (info.sum === 0) { + return (node.viewChildren = []); + } + + // Set area to each child. + for (var i = 0, len = viewChildren.length; i < len; i++) { + var area = viewChildren[i].getValue() / info.sum * totalArea; + // Do not use setLayout({...}, true), because it is needed to clear last layout. + viewChildren[i].setLayout({area: area}); + } + + if (overLeafDepth) { + viewChildren.length && node.setLayout({isLeafRoot: true}, true); + viewChildren.length = 0; + } + + node.viewChildren = viewChildren; + node.setLayout({dataExtent: info.dataExtent}, true); + + return viewChildren; + } + + /** + * Consider 'visibleMin'. Modify viewChildren and get new sum. + */ + function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) { + + // visibleMin is not supported yet when no option.sort. + if (!orderBy) { + return sum; + } + + var visibleMin = nodeModel.get('visibleMin'); + var len = orderedChildren.length; + var deletePoint = len; + + // Always travel from little value to big value. + for (var i = len - 1; i >= 0; i--) { + var value = orderedChildren[ + orderBy === 'asc' ? len - i - 1 : i + ].getValue(); + + if (value / sum * totalArea < visibleMin) { + deletePoint = i; + sum -= value; + } + } + + orderBy === 'asc' + ? orderedChildren.splice(0, len - deletePoint) + : orderedChildren.splice(deletePoint, len - deletePoint); + + return sum; + } + + /** + * Sort + */ + function sort(viewChildren, orderBy) { + if (orderBy) { + viewChildren.sort(function (a, b) { + return orderBy === 'asc' + ? a.getValue() - b.getValue() : b.getValue() - a.getValue(); + }); + } + return viewChildren; + } + + /** + * Statistic + */ + function statistic(nodeModel, children, orderBy) { + // Calculate sum. + var sum = 0; + for (var i = 0, len = children.length; i < len; i++) { + sum += children[i].getValue(); + } + + // Statistic data extent for latter visual coding. + // Notice: data extent should be calculate based on raw children + // but not filtered view children, otherwise visual mapping will not + // be stable when zoom (where children is filtered by visibleMin). + + var dimension = nodeModel.get('visualDimension'); + var dataExtent; + + // The same as area dimension. + if (!children || !children.length) { + dataExtent = [NaN, NaN]; + } + else if (dimension === 'value' && orderBy) { + dataExtent = [ + children[children.length - 1].getValue(), + children[0].getValue() + ]; + orderBy === 'asc' && dataExtent.reverse(); + } + // Other dimension. + else { + var dataExtent = [Infinity, -Infinity]; + each(children, function (child) { + var value = child.getValue(dimension); + value < dataExtent[0] && (dataExtent[0] = value); + value > dataExtent[1] && (dataExtent[1] = value); + }); + } + + return {sum: sum, dataExtent: dataExtent}; + } + + /** + * Computes the score for the specified row, + * as the worst aspect ratio. + */ + function worst(row, rowFixedLength, ratio) { + var areaMax = 0; + var areaMin = Infinity; + + for (var i = 0, area, len = row.length; i < len; i++) { + area = row[i].getLayout().area; + if (area) { + area < areaMin && (areaMin = area); + area > areaMax && (areaMax = area); + } + } + + var squareArea = row.area * row.area; + var f = rowFixedLength * rowFixedLength * ratio; + + return squareArea + ? mathMax( + (f * areaMax) / squareArea, + squareArea / (f * areaMin) + ) + : Infinity; + } + + /** + * Positions the specified row of nodes. Modifies `rect`. + */ + function position(row, rowFixedLength, rect, halfGapWidth, flush) { + // When rowFixedLength === rect.width, + // it is horizontal subdivision, + // rowFixedLength is the width of the subdivision, + // rowOtherLength is the height of the subdivision, + // and nodes will be positioned from left to right. + + // wh[idx0WhenH] means: when horizontal, + // wh[idx0WhenH] => wh[0] => 'width'. + // xy[idx1WhenH] => xy[1] => 'y'. + var idx0WhenH = rowFixedLength === rect.width ? 0 : 1; + var idx1WhenH = 1 - idx0WhenH; + var xy = ['x', 'y']; + var wh = ['width', 'height']; + + var last = rect[xy[idx0WhenH]]; + var rowOtherLength = rowFixedLength + ? row.area / rowFixedLength : 0; + + if (flush || rowOtherLength > rect[wh[idx1WhenH]]) { + rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow + } + for (var i = 0, rowLen = row.length; i < rowLen; i++) { + var node = row[i]; + var nodeLayout = {}; + var step = rowOtherLength + ? node.getLayout().area / rowOtherLength : 0; + + var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax(rowOtherLength - 2 * halfGapWidth, 0); + + // We use Math.max/min to avoid negative width/height when considering gap width. + var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last; + var modWH = (i === rowLen - 1 || remain < step) ? remain : step; + var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax(modWH - 2 * halfGapWidth, 0); + + nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin(halfGapWidth, wh1 / 2); + nodeLayout[xy[idx0WhenH]] = last + mathMin(halfGapWidth, wh0 / 2); + + last += modWH; + node.setLayout(nodeLayout, true); + } + + rect[xy[idx1WhenH]] += rowOtherLength; + rect[wh[idx1WhenH]] -= rowOtherLength; + } + + // Return [containerWidth, containerHeight] as defualt. + function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) { + // If targetInfo.node exists, we zoom to the node, + // so estimate whold width and heigth by target node. + var currNode = (targetInfo || {}).node; + var defaultSize = [containerWidth, containerHeight]; + + if (!currNode || currNode === viewRoot) { + return defaultSize; + } + + var parent; + var viewArea = containerWidth * containerHeight; + var area = viewArea * seriesModel.option.zoomToNodeRatio; + + while (parent = currNode.parentNode) { // jshint ignore:line + var sum = 0; + var siblings = parent.children; + + for (var i = 0, len = siblings.length; i < len; i++) { + sum += siblings[i].getValue(); + } + var currNodeValue = currNode.getValue(); + if (currNodeValue === 0) { + return defaultSize; + } + area *= sum / currNodeValue; + + var borderWidth = parent.getModel('itemStyle.normal').get('borderWidth'); + + if (isFinite(borderWidth)) { + // Considering border, suppose aspect ratio is 1. + area += 4 * borderWidth * borderWidth + 4 * borderWidth * Math.pow(area, 0.5); + } + + area > numberUtil.MAX_SAFE_INTEGER && (area = numberUtil.MAX_SAFE_INTEGER); + + currNode = parent; + } + + area < viewArea && (area = viewArea); + var scale = Math.pow(area / viewArea, 0.5); + + return [containerWidth * scale, containerHeight * scale]; + } + + // Root postion base on coord of containerGroup + function calculateRootPosition(layoutInfo, rootRect, targetInfo) { + if (rootRect) { + return {x: rootRect.x, y: rootRect.y}; + } + + var defaultPosition = {x: 0, y: 0}; + if (!targetInfo) { + return defaultPosition; + } + + // If targetInfo is fetched by 'retrieveTargetInfo', + // old tree and new tree are the same tree, + // so the node still exists and we can visit it. + + var targetNode = targetInfo.node; + var layout = targetNode.getLayout(); + + if (!layout) { + return defaultPosition; + } + + // Transform coord from local to container. + var targetCenter = [layout.width / 2, layout.height / 2]; + var node = targetNode; + while (node) { + var nodeLayout = node.getLayout(); + targetCenter[0] += nodeLayout.x; + targetCenter[1] += nodeLayout.y; + node = node.parentNode; + } + + return { + x: layoutInfo.width / 2 - targetCenter[0], + y: layoutInfo.height / 2 - targetCenter[1] + }; + } + + // Mark nodes visible for prunning when visual coding and rendering. + // Prunning depends on layout and root position, so we have to do it after layout. + function prunning(node, clipRect, viewAbovePath, viewRoot, depth) { + var nodeLayout = node.getLayout(); + var nodeInViewAbovePath = viewAbovePath[depth]; + var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node; + + if ( + (nodeInViewAbovePath && !isAboveViewRoot) + || (depth === viewAbovePath.length && node !== viewRoot) + ) { + return; + } + + node.setLayout({ + // isInView means: viewRoot sub tree + viewAbovePath + isInView: true, + // invisible only means: outside view clip so that the node can not + // see but still layout for animation preparation but not render. + invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout), + isAboveViewRoot: isAboveViewRoot + }, true); + + // Transform to child coordinate. + var childClipRect = new BoundingRect( + clipRect.x - nodeLayout.x, + clipRect.y - nodeLayout.y, + clipRect.width, + clipRect.height + ); + + each(node.viewChildren || [], function (child) { + prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1); + }); + } + + module.exports = update; + + +/***/ }, +/* 194 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + __webpack_require__(195); + __webpack_require__(198); + + __webpack_require__(203); + + echarts.registerProcessor(__webpack_require__(204)); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'graph', 'circle', null + )); + echarts.registerVisual(__webpack_require__(205)); + echarts.registerVisual(__webpack_require__(206)); + + echarts.registerLayout(__webpack_require__(207)); + echarts.registerLayout(__webpack_require__(210)); + echarts.registerLayout(__webpack_require__(212)); + + // Graph view coordinate system + echarts.registerCoordinateSystem('graphView', { + create: __webpack_require__(214) + }); + + +/***/ }, +/* 195 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var Model = __webpack_require__(12); + + var createGraphFromNodeEdge = __webpack_require__(196); + + var GraphSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.graph', + + init: function (option) { + GraphSeries.superApply(this, 'init', arguments); + + // Provide data for legend select + this.legendDataProvider = function () { + return this._categoriesData; + }; + + this.fillDataTextStyle(option.edges || option.links); + + this._updateCategoriesData(); + }, + + mergeOption: function (option) { + GraphSeries.superApply(this, 'mergeOption', arguments); + + this.fillDataTextStyle(option.edges || option.links); + + this._updateCategoriesData(); + }, + + mergeDefaultAndTheme: function (option) { + GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments); + modelUtil.defaultEmphasis(option.edgeLabel, modelUtil.LABEL_OPTIONS); + }, + + getInitialData: function (option, ecModel) { + var edges = option.edges || option.links || []; + var nodes = option.data || option.nodes || []; + var self = this; + + if (nodes && edges) { + return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data; + } + + function beforeLink(nodeData, edgeData) { + // Overwrite nodeData.getItemModel to + nodeData.wrapMethod('getItemModel', function (model) { + var categoriesModels = self._categoriesModels; + var categoryIdx = model.getShallow('category'); + var categoryModel = categoriesModels[categoryIdx]; + if (categoryModel) { + categoryModel.parentModel = model.parentModel; + model.parentModel = categoryModel; + } + return model; + }); + + var edgeLabelModel = self.getModel('edgeLabel'); + var wrappedGetEdgeModel = function (path, parentModel) { + var pathArr = (path || '').split('.'); + if (pathArr[0] === 'label') { + parentModel = parentModel + || edgeLabelModel.getModel(pathArr.slice(1)); + } + var model = Model.prototype.getModel.call(this, pathArr, parentModel); + model.getModel = wrappedGetEdgeModel; + return model; + }; + edgeData.wrapMethod('getItemModel', function (model) { + // FIXME Wrap get method ? + model.getModel = wrappedGetEdgeModel; + return model; + }); + } + }, + + /** + * @return {module:echarts/data/Graph} + */ + getGraph: function () { + return this.getData().graph; + }, + + /** + * @return {module:echarts/data/List} + */ + getEdgeData: function () { + return this.getGraph().edgeData; + }, + + /** + * @return {module:echarts/data/List} + */ + getCategoriesData: function () { + return this._categoriesData; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + if (dataType === 'edge') { + var nodeData = this.getData(); + var params = this.getDataParams(dataIndex, dataType); + var edge = nodeData.graph.getEdgeByIndex(dataIndex); + var sourceName = nodeData.getName(edge.node1.dataIndex); + var targetName = nodeData.getName(edge.node2.dataIndex); + var html = sourceName + ' > ' + targetName; + if (params.value) { + html += ' : ' + params.value; + } + return html; + } + else { // dataType === 'node' or empty + return GraphSeries.superApply(this, 'formatTooltip', arguments); + } + }, + + _updateCategoriesData: function () { + var categories = zrUtil.map(this.option.categories || [], function (category) { + // Data must has value + return category.value != null ? category : zrUtil.extend({ + value: 0 + }, category); + }); + var categoriesData = new List(['value'], this); + categoriesData.initData(categories); + + this._categoriesData = categoriesData; + + this._categoriesModels = categoriesData.mapArray(function (idx) { + return categoriesData.getItemModel(idx, true); + }); + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + }, + + ifEnableAnimation: function () { + return GraphSeries.superCall(this, 'ifEnableAnimation') + // Not enable animation when do force layout + && !(this.get('layout') === 'force' && this.get('force.layoutAnimation')); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + coordinateSystem: 'view', + + // Default option for all coordinate systems + // xAxisIndex: 0, + // yAxisIndex: 0, + // polarIndex: 0, + // geoIndex: 0, + + legendHoverLink: true, + + hoverAnimation: true, + + layout: null, + + focusNodeAdjacency: false, + + // Configuration of circular layout + circular: { + rotateLabel: false + }, + // Configuration of force directed layout + force: { + initLayout: null, + // Node repulsion. Can be an array to represent range. + repulsion: [0, 50], + gravity: 0.1, + + // Edge length. Can be an array to represent range. + edgeLength: 30, + + layoutAnimation: true + }, + + left: 'center', + top: 'center', + // right: null, + // bottom: null, + // width: '80%', + // height: '80%', + + symbol: 'circle', + symbolSize: 10, + + edgeSymbol: ['none', 'none'], + edgeSymbolSize: 10, + edgeLabel: { + normal: { + position: 'middle' + }, + emphasis: {} + }, + + draggable: false, + + roam: false, + + // Default on center of graph + center: null, + + zoom: 1, + // Symbol size scale ratio in roam + nodeScaleRatio: 0.6, + + // categories: [], + + // data: [] + // Or + // nodes: [] + // + // links: [] + // Or + // edges: [] + + label: { + normal: { + show: false, + formatter: '{b}' + }, + emphasis: { + show: true + } + }, + + itemStyle: { + normal: {}, + emphasis: {} + }, + + lineStyle: { + normal: { + color: '#aaa', + width: 1, + curveness: 0, + opacity: 0.5 + }, + emphasis: {} + } + } + }); + + module.exports = GraphSeries; + + +/***/ }, +/* 196 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var Graph = __webpack_require__(197); + var linkList = __webpack_require__(185); + var completeDimensions = __webpack_require__(102); + var CoordinateSystem = __webpack_require__(26); + var zrUtil = __webpack_require__(4); + var createListFromArray = __webpack_require__(101); + + module.exports = function (nodes, edges, hostModel, directed, beforeLink) { + var graph = new Graph(directed); + for (var i = 0; i < nodes.length; i++) { + graph.addNode(zrUtil.retrieve( + // Id, name, dataIndex + nodes[i].id, nodes[i].name, i + ), i); + } + + var linkNameList = []; + var validEdges = []; + var linkCount = 0; + for (var i = 0; i < edges.length; i++) { + var link = edges[i]; + var source = link.source; + var target = link.target; + // addEdge may fail when source or target not exists + if (graph.addEdge(source, target, linkCount)) { + validEdges.push(link); + linkNameList.push(zrUtil.retrieve(link.id, source + ' > ' + target)); + linkCount++; + } + } + + var coordSys = hostModel.get('coordinateSystem'); + var nodeData; + if (coordSys === 'cartesian2d' || coordSys === 'polar') { + nodeData = createListFromArray(nodes, hostModel, hostModel.ecModel); + } + else { + // FIXME + var coordSysCtor = CoordinateSystem.get(coordSys); + // FIXME + var dimensionNames = completeDimensions( + ((coordSysCtor && coordSysCtor.type !== 'view') ? (coordSysCtor.dimensions || []) : []).concat(['value']), + nodes + ); + nodeData = new List(dimensionNames, hostModel); + nodeData.initData(nodes); + } + + var edgeData = new List(['value'], hostModel); + edgeData.initData(validEdges, linkNameList); + + beforeLink && beforeLink(nodeData, edgeData); + + linkList({ + mainData: nodeData, + struct: graph, + structAttr: 'graph', + datas: {node: nodeData, edge: edgeData}, + datasAttr: {node: 'data', edge: 'edgeData'} + }); + + // Update dataIndex of nodes and edges because invalid edge may be removed + graph.update(); + + return graph; + }; + + +/***/ }, +/* 197 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Graph data structure + * + * @module echarts/data/Graph + * @author Yi Shen(https://www.github.com/pissang) + */ + + + var zrUtil = __webpack_require__(4); + + /** + * @alias module:echarts/data/Graph + * @constructor + * @param {boolean} directed + */ + var Graph = function(directed) { + /** + * 是否是有向图 + * @type {boolean} + * @private + */ + this._directed = directed || false; + + /** + * @type {Array.} + * @readOnly + */ + this.nodes = []; + + /** + * @type {Array.} + * @readOnly + */ + this.edges = []; + + /** + * @type {Object.} + * @private + */ + this._nodesMap = {}; + /** + * @type {Object.} + * @private + */ + this._edgesMap = {}; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.data; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.edgeData; + }; + + var graphProto = Graph.prototype; + /** + * @type {string} + */ + graphProto.type = 'graph'; + + /** + * If is directed graph + * @return {boolean} + */ + graphProto.isDirected = function () { + return this._directed; + }; + + /** + * Add a new node + * @param {string} id + * @param {number} [dataIndex] + */ + graphProto.addNode = function (id, dataIndex) { + id = id || ('' + dataIndex); + + var nodesMap = this._nodesMap; + + if (nodesMap[id]) { + return; + } + + var node = new Node(id, dataIndex); + node.hostGraph = this; + + this.nodes.push(node); + + nodesMap[id] = node; + return node; + }; + + /** + * Get node by data index + * @param {number} dataIndex + * @return {module:echarts/data/Graph~Node} + */ + graphProto.getNodeByIndex = function (dataIndex) { + var rawIdx = this.data.getRawIndex(dataIndex); + return this.nodes[rawIdx]; + }; + /** + * Get node by id + * @param {string} id + * @return {module:echarts/data/Graph.Node} + */ + graphProto.getNodeById = function (id) { + return this._nodesMap[id]; + }; + + /** + * Add a new edge + * @param {number|string|module:echarts/data/Graph.Node} n1 + * @param {number|string|module:echarts/data/Graph.Node} n2 + * @param {number} [dataIndex=-1] + * @return {module:echarts/data/Graph.Edge} + */ + graphProto.addEdge = function (n1, n2, dataIndex) { + var nodesMap = this._nodesMap; + var edgesMap = this._edgesMap; + + // PNEDING + if (typeof n1 === 'number') { + n1 = this.nodes[n1]; + } + if (typeof n2 === 'number') { + n2 = this.nodes[n2]; + } + + if (!(n1 instanceof Node)) { + n1 = nodesMap[n1]; + } + if (!(n2 instanceof Node)) { + n2 = nodesMap[n2]; + } + if (!n1 || !n2) { + return; + } + + var key = n1.id + '-' + n2.id; + // PENDING + if (edgesMap[key]) { + return; + } + + var edge = new Edge(n1, n2, dataIndex); + edge.hostGraph = this; + + if (this._directed) { + n1.outEdges.push(edge); + n2.inEdges.push(edge); + } + n1.edges.push(edge); + if (n1 !== n2) { + n2.edges.push(edge); + } + + this.edges.push(edge); + edgesMap[key] = edge; + + return edge; + }; + + /** + * Get edge by data index + * @param {number} dataIndex + * @return {module:echarts/data/Graph~Node} + */ + graphProto.getEdgeByIndex = function (dataIndex) { + var rawIdx = this.edgeData.getRawIndex(dataIndex); + return this.edges[rawIdx]; + }; + /** + * Get edge by two linked nodes + * @param {module:echarts/data/Graph.Node|string} n1 + * @param {module:echarts/data/Graph.Node|string} n2 + * @return {module:echarts/data/Graph.Edge} + */ + graphProto.getEdge = function (n1, n2) { + if (n1 instanceof Node) { + n1 = n1.id; + } + if (n2 instanceof Node) { + n2 = n2.id; + } + + var edgesMap = this._edgesMap; + + if (this._directed) { + return edgesMap[n1 + '-' + n2]; + } else { + return edgesMap[n1 + '-' + n2] + || edgesMap[n2 + '-' + n1]; + } + }; + + /** + * Iterate all nodes + * @param {Function} cb + * @param {*} [context] + */ + graphProto.eachNode = function (cb, context) { + var nodes = this.nodes; + var len = nodes.length; + for (var i = 0; i < len; i++) { + if (nodes[i].dataIndex >= 0) { + cb.call(context, nodes[i], i); + } + } + }; + + /** + * Iterate all edges + * @param {Function} cb + * @param {*} [context] + */ + graphProto.eachEdge = function (cb, context) { + var edges = this.edges; + var len = edges.length; + for (var i = 0; i < len; i++) { + if (edges[i].dataIndex >= 0 + && edges[i].node1.dataIndex >= 0 + && edges[i].node2.dataIndex >= 0 + ) { + cb.call(context, edges[i], i); + } + } + }; + + /** + * Breadth first traverse + * @param {Function} cb + * @param {module:echarts/data/Graph.Node} startNode + * @param {string} [direction='none'] 'none'|'in'|'out' + * @param {*} [context] + */ + graphProto.breadthFirstTraverse = function ( + cb, startNode, direction, context + ) { + if (!(startNode instanceof Node)) { + startNode = this._nodesMap[startNode]; + } + if (!startNode) { + return; + } + + var edgeType = direction === 'out' + ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges'); + + for (var i = 0; i < this.nodes.length; i++) { + this.nodes[i].__visited = false; + } + + if (cb.call(context, startNode, null)) { + return; + } + + var queue = [startNode]; + while (queue.length) { + var currentNode = queue.shift(); + var edges = currentNode[edgeType]; + + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + var otherNode = e.node1 === currentNode + ? e.node2 : e.node1; + if (!otherNode.__visited) { + if (cb.call(otherNode, otherNode, currentNode)) { + // Stop traversing + return; + } + queue.push(otherNode); + otherNode.__visited = true; + } + } + } + }; + + // TODO + // graphProto.depthFirstTraverse = function ( + // cb, startNode, direction, context + // ) { + + // }; + + // Filter update + graphProto.update = function () { + var data = this.data; + var edgeData = this.edgeData; + var nodes = this.nodes; + var edges = this.edges; + + for (var i = 0, len = nodes.length; i < len; i++) { + nodes[i].dataIndex = -1; + } + for (var i = 0, len = data.count(); i < len; i++) { + nodes[data.getRawIndex(i)].dataIndex = i; + } + + edgeData.filterSelf(function (idx) { + var edge = edges[edgeData.getRawIndex(idx)]; + return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0; + }); + + // Update edge + for (var i = 0, len = edges.length; i < len; i++) { + edges[i].dataIndex = -1; + } + for (var i = 0, len = edgeData.count(); i < len; i++) { + edges[edgeData.getRawIndex(i)].dataIndex = i; + } + }; + + /** + * @return {module:echarts/data/Graph} + */ + graphProto.clone = function () { + var graph = new Graph(this._directed); + var nodes = this.nodes; + var edges = this.edges; + for (var i = 0; i < nodes.length; i++) { + graph.addNode(nodes[i].id, nodes[i].dataIndex); + } + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + graph.addEdge(e.node1.id, e.node2.id, e.dataIndex); + } + return graph; + }; + + + /** + * @alias module:echarts/data/Graph.Node + */ + function Node(id, dataIndex) { + /** + * @type {string} + */ + this.id = id == null ? '' : id; + + /** + * @type {Array.} + */ + this.inEdges = []; + /** + * @type {Array.} + */ + this.outEdges = []; + /** + * @type {Array.} + */ + this.edges = []; + /** + * @type {module:echarts/data/Graph} + */ + this.hostGraph; + + /** + * @type {number} + */ + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + + Node.prototype = { + + constructor: Node, + + /** + * @return {number} + */ + degree: function () { + return this.edges.length; + }, + + /** + * @return {number} + */ + inDegree: function () { + return this.inEdges.length; + }, + + /** + * @return {number} + */ + outDegree: function () { + return this.outEdges.length; + }, + + /** + * @param {string} [path] + * @return {module:echarts/model/Model} + */ + getModel: function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.data.getItemModel(this.dataIndex); + + return itemModel.getModel(path); + } + }; + + /** + * 图边 + * @alias module:echarts/data/Graph.Edge + * @param {module:echarts/data/Graph.Node} n1 + * @param {module:echarts/data/Graph.Node} n2 + * @param {number} [dataIndex=-1] + */ + function Edge(n1, n2, dataIndex) { + + /** + * 节点1,如果是有向图则为源节点 + * @type {module:echarts/data/Graph.Node} + */ + this.node1 = n1; + + /** + * 节点2,如果是有向图则为目标节点 + * @type {module:echarts/data/Graph.Node} + */ + this.node2 = n2; + + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + + /** + * @param {string} [path] + * @return {module:echarts/model/Model} + */ + Edge.prototype.getModel = function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.edgeData.getItemModel(this.dataIndex); + + return itemModel.getModel(path); + }; + + var createGraphDataProxyMixin = function (hostName, dataName) { + return { + /** + * @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'. + * @return {number} + */ + getValue: function (dimension) { + var data = this[hostName][dataName]; + return data.get(data.getDimension(dimension || 'value'), this.dataIndex); + }, + + /** + * @param {Object|string} key + * @param {*} [value] + */ + setVisual: function (key, value) { + this.dataIndex >= 0 + && this[hostName][dataName].setItemVisual(this.dataIndex, key, value); + }, + + /** + * @param {string} key + * @return {boolean} + */ + getVisual: function (key, ignoreParent) { + return this[hostName][dataName].getItemVisual(this.dataIndex, key, ignoreParent); + }, + + /** + * @param {Object} layout + * @return {boolean} [merge=false] + */ + setLayout: function (layout, merge) { + this.dataIndex >= 0 + && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge); + }, + + /** + * @return {Object} + */ + getLayout: function () { + return this[hostName][dataName].getItemLayout(this.dataIndex); + }, + + /** + * @return {module:zrender/Element} + */ + getGraphicEl: function () { + return this[hostName][dataName].getItemGraphicEl(this.dataIndex); + }, + + /** + * @return {number} + */ + getRawIndex: function () { + return this[hostName][dataName].getRawIndex(this.dataIndex); + } + }; + }; + + zrUtil.mixin(Node, createGraphDataProxyMixin('hostGraph', 'data')); + zrUtil.mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData')); + + Graph.Node = Node; + Graph.Edge = Edge; + + module.exports = Graph; + + +/***/ }, +/* 198 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var SymbolDraw = __webpack_require__(104); + var LineDraw = __webpack_require__(199); + var RoamController = __webpack_require__(174); + + var graphic = __webpack_require__(43); + var adjustEdge = __webpack_require__(202); + var zrUtil = __webpack_require__(4); + + var nodeOpacityPath = ['itemStyle', 'normal', 'opacity']; + var lineOpacityPath = ['lineStyle', 'normal', 'opacity']; + + function getItemOpacity(item, opacityPath) { + return item.getVisual('opacity') || item.getModel().get(opacityPath); + } + + __webpack_require__(1).extendChartView({ + + type: 'graph', + + init: function (ecModel, api) { + var symbolDraw = new SymbolDraw(); + var lineDraw = new LineDraw(); + var group = this.group; + + var controller = new RoamController(api.getZr(), group); + + group.add(symbolDraw.group); + group.add(lineDraw.group); + + this._symbolDraw = symbolDraw; + this._lineDraw = lineDraw; + this._controller = controller; + + this._firstRender = true; + }, + + render: function (seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + + this._model = seriesModel; + this._nodeScaleRatio = seriesModel.get('nodeScaleRatio'); + + var symbolDraw = this._symbolDraw; + var lineDraw = this._lineDraw; + + var group = this.group; + + if (coordSys.type === 'view') { + var groupNewProp = { + position: coordSys.position, + scale: coordSys.scale + }; + if (this._firstRender) { + group.attr(groupNewProp); + } + else { + graphic.updateProps(group, groupNewProp, seriesModel); + } + } + // Fix edge contact point with node + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + + var data = seriesModel.getData(); + symbolDraw.updateData(data); + + var edgeData = seriesModel.getEdgeData(); + lineDraw.updateData(edgeData); + + this._updateNodeAndLinkScale(); + + this._updateController(seriesModel, api); + + clearTimeout(this._layoutTimeout); + var forceLayout = seriesModel.forceLayout; + var layoutAnimation = seriesModel.get('force.layoutAnimation'); + if (forceLayout) { + this._startForceLayoutIteration(forceLayout, layoutAnimation); + } + data.eachItemGraphicEl(function (el, idx) { + var itemModel = data.getItemModel(idx); + // Update draggable + el.off('drag').off('dragend'); + var draggable = data.getItemModel(idx).get('draggable'); + if (draggable) { + el.on('drag', function () { + if (forceLayout) { + forceLayout.warmUp(); + !this._layouting + && this._startForceLayoutIteration(forceLayout, layoutAnimation); + forceLayout.setFixed(idx); + // Write position back to layout + data.setItemLayout(idx, el.position); + } + }, this).on('dragend', function () { + if (forceLayout) { + forceLayout.setUnfixed(idx); + } + }, this); + } + el.setDraggable(draggable && forceLayout); + + el.off('mouseover', this._focusNodeAdjacency); + el.off('mouseout', this._unfocusAll); + if (itemModel.get('focusNodeAdjacency')) { + el.on('mouseover', this._focusNodeAdjacency, this); + el.on('mouseout', this._unfocusAll, this); + } + }, this); + + var circularRotateLabel = seriesModel.get('layout') === 'circular' && seriesModel.get('circular.rotateLabel'); + var cx = data.getLayout('cx'); + var cy = data.getLayout('cy'); + data.eachItemGraphicEl(function (el, idx) { + var symbolPath = el.getSymbolPath(); + if (circularRotateLabel) { + var pos = data.getItemLayout(idx); + var rad = Math.atan2(pos[1] - cy, pos[0] - cx); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + var isLeft = pos[0] < cx; + if (isLeft) { + rad = rad - Math.PI; + } + var textPosition = isLeft ? 'left' : 'right'; + symbolPath.setStyle({ + textRotation: rad, + textPosition: textPosition + }); + symbolPath.hoverStyle && (symbolPath.hoverStyle.textPosition = textPosition); + } + else { + symbolPath.setStyle({ + textRotation: 0 + }); + } + }); + + this._firstRender = false; + }, + + _focusNodeAdjacency: function (e) { + var data = this._model.getData(); + var graph = data.graph; + var el = e.target; + var dataIndex = el.dataIndex; + var dataType = el.dataType; + + function fadeOutItem(item, opacityPath) { + var opacity = getItemOpacity(item, opacityPath); + var el = item.getGraphicEl(); + if (opacity == null) { + opacity = 1; + } + + el.traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity * 0.1); + } + }); + } + + function fadeInItem(item, opacityPath) { + var opacity = getItemOpacity(item, opacityPath); + var el = item.getGraphicEl(); + + el.traverse(function (child) { + child.trigger('emphasis'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + } + if (dataIndex !== null && dataType !== 'edge') { + graph.eachNode(function (node) { + fadeOutItem(node, nodeOpacityPath); + }); + graph.eachEdge(function (edge) { + fadeOutItem(edge, lineOpacityPath); + }); + + var node = graph.getNodeByIndex(dataIndex); + fadeInItem(node, nodeOpacityPath); + zrUtil.each(node.edges, function (edge) { + if (edge.dataIndex < 0) { + return; + } + fadeInItem(edge, lineOpacityPath); + fadeInItem(edge.node1, nodeOpacityPath); + fadeInItem(edge.node2, nodeOpacityPath); + }); + } + }, + + _unfocusAll: function () { + var data = this._model.getData(); + var graph = data.graph; + graph.eachNode(function (node) { + var opacity = getItemOpacity(node, nodeOpacityPath); + node.getGraphicEl().traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + }); + graph.eachEdge(function (edge) { + var opacity = getItemOpacity(edge, lineOpacityPath); + edge.getGraphicEl().traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + }); + }, + + _startForceLayoutIteration: function (forceLayout, layoutAnimation) { + var self = this; + (function step() { + forceLayout.step(function (stopped) { + self.updateLayout(self._model); + (self._layouting = !stopped) && ( + layoutAnimation + ? (self._layoutTimeout = setTimeout(step, 16)) + : step() + ); + }); + })(); + }, + + _updateController: function (seriesModel, api) { + var controller = this._controller; + var group = this.group; + controller.rectProvider = function () { + var rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect; + }; + if (seriesModel.coordinateSystem.type !== 'view') { + controller.disable(); + return; + } + controller.enable(seriesModel.get('roam')); + controller.zoomLimit = seriesModel.get('scaleLimit'); + // Update zoom from model + controller.zoom = seriesModel.coordinateSystem.getZoom(); + + controller + .off('pan') + .off('zoom') + .on('pan', function (dx, dy) { + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + dx: dx, + dy: dy + }); + }) + .on('zoom', function (zoom, mouseX, mouseY) { + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + zoom: zoom, + originX: mouseX, + originY: mouseY + }); + this._updateNodeAndLinkScale(); + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + this._lineDraw.updateLayout(); + }, this); + }, + + _updateNodeAndLinkScale: function () { + var seriesModel = this._model; + var data = seriesModel.getData(); + + var nodeScale = this._getNodeGlobalScale(seriesModel); + var invScale = [nodeScale, nodeScale]; + + data.eachItemGraphicEl(function (el, idx) { + el.attr('scale', invScale); + }); + }, + + _getNodeGlobalScale: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type !== 'view') { + return 1; + } + + var nodeScaleRatio = this._nodeScaleRatio; + + var groupScale = coordSys.scale; + var groupZoom = (groupScale && groupScale[0]) || 1; + // Scale node when zoom changes + var roamZoom = coordSys.getZoom(); + var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1; + + return nodeScale / groupZoom; + }, + + updateLayout: function (seriesModel) { + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + + this._symbolDraw.updateLayout(); + this._lineDraw.updateLayout(); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(); + this._lineDraw && this._lineDraw.remove(); + } + }); + + +/***/ }, +/* 199 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/LineDraw + */ + + + var graphic = __webpack_require__(43); + var LineGroup = __webpack_require__(200); + + + function isPointNaN(pt) { + return isNaN(pt[0]) || isNaN(pt[1]); + } + function lineNeedsDraw(pts) { + return !isPointNaN(pts[0]) && !isPointNaN(pts[1]); + } + /** + * @alias module:echarts/component/marker/LineDraw + * @constructor + */ + function LineDraw(ctor) { + this._ctor = ctor || LineGroup; + this.group = new graphic.Group(); + } + + var lineDrawProto = LineDraw.prototype; + + /** + * @param {module:echarts/data/List} lineData + */ + lineDrawProto.updateData = function (lineData) { + + var oldLineData = this._lineData; + var group = this.group; + var LineCtor = this._ctor; + + var hostModel = lineData.hostModel; + + var seriesScope = { + lineStyle: hostModel.getModel('lineStyle.normal').getLineStyle(), + hoverLineStyle: hostModel.getModel('lineStyle.emphasis').getLineStyle(), + labelModel: hostModel.getModel('label.normal'), + hoverLabelModel: hostModel.getModel('label.emphasis') + }; + + lineData.diff(oldLineData) + .add(function (idx) { + if (!lineNeedsDraw(lineData.getItemLayout(idx))) { + return; + } + var lineGroup = new LineCtor(lineData, idx, seriesScope); + + lineData.setItemGraphicEl(idx, lineGroup); + + group.add(lineGroup); + }) + .update(function (newIdx, oldIdx) { + var lineGroup = oldLineData.getItemGraphicEl(oldIdx); + if (!lineNeedsDraw(lineData.getItemLayout(newIdx))) { + group.remove(lineGroup); + return; + } + + if (!lineGroup) { + lineGroup = new LineCtor(lineData, newIdx, seriesScope); + } + else { + lineGroup.updateData(lineData, newIdx, seriesScope); + } + + lineData.setItemGraphicEl(newIdx, lineGroup); + + group.add(lineGroup); + }) + .remove(function (idx) { + group.remove(oldLineData.getItemGraphicEl(idx)); + }) + .execute(); + + this._lineData = lineData; + }; + + lineDrawProto.updateLayout = function () { + var lineData = this._lineData; + lineData.eachItemGraphicEl(function (el, idx) { + el.updateLayout(lineData, idx); + }, this); + }; + + lineDrawProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LineDraw; + + +/***/ }, +/* 200 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Line + */ + + + var symbolUtil = __webpack_require__(106); + var vector = __webpack_require__(10); + // var matrix = require('zrender/lib/core/matrix'); + var LinePath = __webpack_require__(201); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol']; + function makeSymbolTypeKey(symbolCategory) { + return '_' + symbolCategory + 'Type'; + } + /** + * @inner + */ + function createSymbol(name, lineData, idx) { + var color = lineData.getItemVisual(idx, 'color'); + var symbolType = lineData.getItemVisual(idx, name); + var symbolSize = lineData.getItemVisual(idx, name + 'Size'); + + if (!symbolType || symbolType === 'none') { + return; + } + + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [symbolSize, symbolSize]; + } + var symbolPath = symbolUtil.createSymbol( + symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, + symbolSize[0], symbolSize[1], color + ); + symbolPath.name = name; + + return symbolPath; + } + + function createLine(points) { + var line = new LinePath({ + name: 'line' + }); + setLinePoints(line.shape, points); + return line; + } + + function setLinePoints(targetShape, points) { + var p1 = points[0]; + var p2 = points[1]; + var cp1 = points[2]; + targetShape.x1 = p1[0]; + targetShape.y1 = p1[1]; + targetShape.x2 = p2[0]; + targetShape.y2 = p2[1]; + targetShape.percent = 1; + + if (cp1) { + targetShape.cpx1 = cp1[0]; + targetShape.cpy1 = cp1[1]; + } + else { + targetShape.cpx1 = NaN; + targetShape.cpy1 = NaN; + } + } + + function updateSymbolAndLabelBeforeLineUpdate () { + var lineGroup = this; + var symbolFrom = lineGroup.childOfName('fromSymbol'); + var symbolTo = lineGroup.childOfName('toSymbol'); + var label = lineGroup.childOfName('label'); + // Quick reject + if (!symbolFrom && !symbolTo && label.ignore) { + return; + } + + var invScale = 1; + var parentNode = this.parent; + while (parentNode) { + if (parentNode.scale) { + invScale /= parentNode.scale[0]; + } + parentNode = parentNode.parent; + } + + var line = lineGroup.childOfName('line'); + // If line not changed + // FIXME Parent scale changed + if (!this.__dirty && !line.__dirty) { + return; + } + + var percent = line.shape.percent; + var fromPos = line.pointAt(0); + var toPos = line.pointAt(percent); + + var d = vector.sub([], toPos, fromPos); + vector.normalize(d, d); + + if (symbolFrom) { + symbolFrom.attr('position', fromPos); + var tangent = line.tangentAt(0); + symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2( + tangent[1], tangent[0] + )); + symbolFrom.attr('scale', [invScale * percent, invScale * percent]); + } + if (symbolTo) { + symbolTo.attr('position', toPos); + var tangent = line.tangentAt(1); + symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2( + tangent[1], tangent[0] + )); + symbolTo.attr('scale', [invScale * percent, invScale * percent]); + } + + if (!label.ignore) { + label.attr('position', toPos); + + var textPosition; + var textAlign; + var textVerticalAlign; + + var distance = 5 * invScale; + // End + if (label.__position === 'end') { + textPosition = [d[0] * distance + toPos[0], d[1] * distance + toPos[1]]; + textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center'); + textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle'); + } + // Middle + else if (label.__position === 'middle') { + var halfPercent = percent / 2; + var tangent = line.tangentAt(halfPercent); + var n = [tangent[1], -tangent[0]]; + var cp = line.pointAt(halfPercent); + if (n[1] > 0) { + n[0] = -n[0]; + n[1] = -n[1]; + } + textPosition = [cp[0] + n[0] * distance, cp[1] + n[1] * distance]; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + var rotation = -Math.atan2(tangent[1], tangent[0]); + if (toPos[0] < fromPos[0]) { + rotation = Math.PI + rotation; + } + label.attr('rotation', rotation); + } + // Start + else { + textPosition = [-d[0] * distance + fromPos[0], -d[1] * distance + fromPos[1]]; + textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center'); + textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle'); + } + label.attr({ + style: { + // Use the user specified text align and baseline first + textVerticalAlign: label.__verticalAlign || textVerticalAlign, + textAlign: label.__textAlign || textAlign + }, + position: textPosition, + scale: [invScale, invScale] + }); + } + } + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Line} + */ + function Line(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this._createLine(lineData, idx, seriesScope); + } + + var lineProto = Line.prototype; + + // Update symbol position and rotation + lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate; + + lineProto._createLine = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var linePoints = lineData.getItemLayout(idx); + + var line = createLine(linePoints); + line.shape.percent = 0; + graphic.initProps(line, { + shape: { + percent: 1 + } + }, seriesModel, idx); + + this.add(line); + + var label = new graphic.Text({ + name: 'label' + }); + this.add(label); + + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = createSymbol(symbolCategory, lineData, idx); + // symbols must added after line to make sure + // it will be updated after line#update. + // Or symbol position and rotation update in line#beforeUpdate will be one frame slow + this.add(symbol); + this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory); + }, this); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + lineProto.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childOfName('line'); + var linePoints = lineData.getItemLayout(idx); + var target = { + shape: {} + }; + setLinePoints(target.shape, linePoints); + graphic.updateProps(line, target, seriesModel, idx); + + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbolType = lineData.getItemVisual(idx, symbolCategory); + var key = makeSymbolTypeKey(symbolCategory); + // Symbol changed + if (this[key] !== symbolType) { + this.remove(this.childOfName(symbolCategory)); + var symbol = createSymbol(symbolCategory, lineData, idx); + this.add(symbol); + } + this[key] = symbolType; + }, this); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + lineProto._updateCommonStl = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childOfName('line'); + + var lineStyle = seriesScope && seriesScope.lineStyle; + var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; + var labelModel = seriesScope && seriesScope.labelModel; + var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; + + // Optimization for large dataset + if (!seriesScope || lineData.hasItemOption) { + var itemModel = lineData.getItemModel(idx); + + lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle(); + hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + + labelModel = itemModel.getModel('label.normal'); + hoverLabelModel = itemModel.getModel('label.emphasis'); + } + + var visualColor = lineData.getItemVisual(idx, 'color'); + var visualOpacity = zrUtil.retrieve( + lineData.getItemVisual(idx, 'opacity'), + lineStyle.opacity, + 1 + ); + if (isNaN(defaultText)) { + // Use name + defaultText = lineData.getName(idx); + } + line.useStyle(zrUtil.defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor, + opacity: visualOpacity + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + // Update symbol + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = this.childOfName(symbolCategory); + if (symbol) { + symbol.setColor(visualColor); + symbol.setStyle({ + opacity: visualOpacity + }); + } + }, this); + + var showLabel = labelModel.getShallow('show'); + var hoverShowLabel = hoverLabelModel.getShallow('show'); + var defaultText; + var label = this.childOfName('label'); + var defaultLabelColor; + if (showLabel || hoverShowLabel) { + defaultText = numberUtil.round(seriesModel.getRawValue(idx)); + defaultLabelColor = visualColor || '#000'; + } + // label.afterUpdate = lineAfterUpdate; + if (showLabel) { + var textStyleModel = labelModel.getModel('textStyle'); + label.setStyle({ + text: zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType), + defaultText + ), + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() || defaultLabelColor + }); + + label.__textAlign = textStyleModel.get('align'); + label.__verticalAlign = textStyleModel.get('baseline'); + label.__position = labelModel.get('position'); + } + else { + label.setStyle('text', ''); + } + if (hoverShowLabel) { + var textStyleHoverModel = hoverLabelModel.getModel('textStyle'); + + label.hoverStyle = { + text: zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), + defaultText + ), + textFont: textStyleHoverModel.getFont(), + fill: textStyleHoverModel.getTextColor() || defaultLabelColor + }; + } + else { + label.hoverStyle = { + text: '' + }; + } + + label.ignore = !showLabel && !hoverShowLabel; + + graphic.setHoverStyle(this); + }; + + lineProto.updateLayout = function (lineData, idx) { + this.setLinePoints(lineData.getItemLayout(idx)); + }; + + lineProto.setLinePoints = function (points) { + var linePath = this.childOfName('line'); + setLinePoints(linePath.shape, points); + linePath.dirty(); + }; + + zrUtil.inherits(Line, graphic.Group); + + module.exports = Line; + + +/***/ }, +/* 201 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Line path for bezier and straight line draw + */ + + var graphic = __webpack_require__(43); + var vec2 = __webpack_require__(10); + + var straightLineProto = graphic.Line.prototype; + var bezierCurveProto = graphic.BezierCurve.prototype; + + function isLine(shape) { + return isNaN(+shape.cpx1) || isNaN(+shape.cpy1); + } + + module.exports = graphic.extendShape({ + + type: 'ec-line', + + style: { + stroke: '#000', + fill: null + }, + + shape: { + x1: 0, + y1: 0, + x2: 0, + y2: 0, + percent: 1, + cpx1: null, + cpy1: null + }, + + buildPath: function (ctx, shape) { + (isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape); + }, + + pointAt: function (t) { + return isLine(this.shape) + ? straightLineProto.pointAt.call(this, t) + : bezierCurveProto.pointAt.call(this, t); + }, + + tangentAt: function (t) { + var shape = this.shape; + var p = isLine(shape) + ? [shape.x2 - shape.x1, shape.y2 - shape.y1] + : bezierCurveProto.tangentAt.call(this, t); + return vec2.normalize(p, p); + } + }); + + +/***/ }, +/* 202 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curveTool = __webpack_require__(50); + var vec2 = __webpack_require__(10); + + var v1 = []; + var v2 = []; + var v3 = []; + var quadraticAt = curveTool.quadraticAt; + var v2DistSquare = vec2.distSquare; + var mathAbs = Math.abs; + function intersectCurveCircle(curvePoints, center, radius) { + var p0 = curvePoints[0]; + var p1 = curvePoints[1]; + var p2 = curvePoints[2]; + + var d = Infinity; + var t; + var radiusSquare = radius * radius; + var interval = 0.1; + + for (var _t = 0.1; _t <= 0.9; _t += 0.1) { + v1[0] = quadraticAt(p0[0], p1[0], p2[0], _t); + v1[1] = quadraticAt(p0[1], p1[1], p2[1], _t); + var diff = mathAbs(v2DistSquare(v1, center) - radiusSquare); + if (diff < d) { + d = diff; + t = _t; + } + } + + // Assume the segment is monotone,Find root through Bisection method + // At most 32 iteration + for (var i = 0; i < 32; i++) { + // var prev = t - interval; + var next = t + interval; + // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev); + // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev); + v2[0] = quadraticAt(p0[0], p1[0], p2[0], t); + v2[1] = quadraticAt(p0[1], p1[1], p2[1], t); + v3[0] = quadraticAt(p0[0], p1[0], p2[0], next); + v3[1] = quadraticAt(p0[1], p1[1], p2[1], next); + + var diff = v2DistSquare(v2, center) - radiusSquare; + if (mathAbs(diff) < 1e-2) { + break; + } + + // var prevDiff = v2DistSquare(v1, center) - radiusSquare; + var nextDiff = v2DistSquare(v3, center) - radiusSquare; + + interval /= 2; + if (diff < 0) { + if (nextDiff >= 0) { + t = t + interval; + } + else { + t = t - interval; + } + } + else { + if (nextDiff >= 0) { + t = t - interval; + } + else { + t = t + interval; + } + } + } + + return t; + } + // Adjust edge to avoid + module.exports = function (graph, scale) { + var tmp0 = []; + var quadraticSubdivide = curveTool.quadraticSubdivide; + var pts = [[], [], []]; + var pts2 = [[], []]; + var v = []; + scale /= 2; + + function getSymbolSize(node) { + var symbolSize = node.getVisual('symbolSize'); + if (symbolSize instanceof Array) { + symbolSize = (symbolSize[0] + symbolSize[1]) / 2; + } + return symbolSize; + } + graph.eachEdge(function (edge, idx) { + var linePoints = edge.getLayout(); + var fromSymbol = edge.getVisual('fromSymbol'); + var toSymbol = edge.getVisual('toSymbol'); + + if (!linePoints.__original) { + linePoints.__original = [ + vec2.clone(linePoints[0]), + vec2.clone(linePoints[1]) + ]; + if (linePoints[2]) { + linePoints.__original.push(vec2.clone(linePoints[2])); + } + } + var originalPoints = linePoints.__original; + // Quadratic curve + if (linePoints[2] != null) { + vec2.copy(pts[0], originalPoints[0]); + vec2.copy(pts[1], originalPoints[2]); + vec2.copy(pts[2], originalPoints[1]); + if (fromSymbol && fromSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node1); + + var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); + // Subdivide and get the second + quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[0][0] = tmp0[3]; + pts[1][0] = tmp0[4]; + quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[0][1] = tmp0[3]; + pts[1][1] = tmp0[4]; + } + if (toSymbol && toSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node2); + + var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); + // Subdivide and get the first + quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[1][0] = tmp0[1]; + pts[2][0] = tmp0[2]; + quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[1][1] = tmp0[1]; + pts[2][1] = tmp0[2]; + } + // Copy back to layout + vec2.copy(linePoints[0], pts[0]); + vec2.copy(linePoints[1], pts[2]); + vec2.copy(linePoints[2], pts[1]); + } + // Line + else { + vec2.copy(pts2[0], originalPoints[0]); + vec2.copy(pts2[1], originalPoints[1]); + + vec2.sub(v, pts2[1], pts2[0]); + vec2.normalize(v, v); + if (fromSymbol && fromSymbol != 'none') { + + var symbolSize = getSymbolSize(edge.node1); + + vec2.scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale); + } + if (toSymbol && toSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node2); + + vec2.scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale); + } + vec2.copy(linePoints[0], pts2[0]); + vec2.copy(linePoints[1], pts2[1]); + } + }); + }; + + +/***/ }, +/* 203 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var roamHelper = __webpack_require__(177); + + var actionInfo = { + type: 'graphRoam', + event: 'graphRoam', + update: 'none' + }; + + /** + * @payload + * @property {string} name Series name + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + + echarts.registerAction(actionInfo, function (payload, ecModel) { + ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + + var res = roamHelper.updateCenterAndZoom(coordSys, payload); + + seriesModel.setCenter + && seriesModel.setCenter(res.center); + + seriesModel.setZoom + && seriesModel.setZoom(res.zoom); + }); + }); + + +/***/ }, +/* 204 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + ecModel.eachSeriesByType('graph', function (graphSeries) { + var categoriesData = graphSeries.getCategoriesData(); + var graph = graphSeries.getGraph(); + var data = graph.data; + + var categoryNames = categoriesData.mapArray(categoriesData.getName); + + data.filterSelf(function (idx) { + var model = data.getItemModel(idx); + var category = model.getShallow('category'); + if (category != null) { + if (typeof category === 'number') { + category = categoryNames[category]; + } + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(category)) { + return false; + } + } + } + return true; + }); + }, this); + }; + + +/***/ }, +/* 205 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + + var paletteScope = {}; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var categoriesData = seriesModel.getCategoriesData(); + var data = seriesModel.getData(); + + var categoryNameIdxMap = {}; + + categoriesData.each(function (idx) { + var name = categoriesData.getName(idx); + categoryNameIdxMap[name] = idx; + + var itemModel = categoriesData.getItemModel(idx); + var color = itemModel.get('itemStyle.normal.color') + || seriesModel.getColorFromPalette(name, paletteScope); + categoriesData.setItemVisual(idx, 'color', color); + }); + + // Assign category color to visual + if (categoriesData.count()) { + data.each(function (idx) { + var model = data.getItemModel(idx); + var category = model.getShallow('category'); + if (category != null) { + if (typeof category === 'string') { + category = categoryNameIdxMap[category]; + } + if (!data.getItemVisual(idx, 'color', true)) { + data.setItemVisual( + idx, 'color', + categoriesData.getItemVisual(category, 'color') + ); + } + } + }); + } + }); + }; + + +/***/ }, +/* 206 */ +/***/ function(module, exports) { + + + + function normalize(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; + } + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var graph = seriesModel.getGraph(); + var edgeData = seriesModel.getEdgeData(); + var symbolType = normalize(seriesModel.get('edgeSymbol')); + var symbolSize = normalize(seriesModel.get('edgeSymbolSize')); + + var colorQuery = 'lineStyle.normal.color'.split('.'); + var opacityQuery = 'lineStyle.normal.opacity'.split('.'); + + edgeData.setVisual('fromSymbol', symbolType && symbolType[0]); + edgeData.setVisual('toSymbol', symbolType && symbolType[1]); + edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); + edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]); + edgeData.setVisual('color', seriesModel.get(colorQuery)); + edgeData.setVisual('opacity', seriesModel.get(opacityQuery)); + + edgeData.each(function (idx) { + var itemModel = edgeData.getItemModel(idx); + var edge = graph.getEdgeByIndex(idx); + var symbolType = normalize(itemModel.getShallow('symbol', true)); + var symbolSize = normalize(itemModel.getShallow('symbolSize', true)); + // Edge visual must after node visual + var color = itemModel.get(colorQuery); + var opacity = itemModel.get(opacityQuery); + switch (color) { + case 'source': + color = edge.node1.getVisual('color'); + break; + case 'target': + color = edge.node2.getVisual('color'); + break; + } + + symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]); + symbolType[1] && edge.setVisual('toSymbol', symbolType[1]); + symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]); + symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]); + + edge.setVisual('color', color); + edge.setVisual('opacity', opacity); + }); + }); + }; + + +/***/ }, +/* 207 */ +/***/ function(module, exports, __webpack_require__) { + + + + var simpleLayoutHelper = __webpack_require__(208); + var simpleLayoutEdge = __webpack_require__(209); + module.exports = function (ecModel, api) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var layout = seriesModel.get('layout'); + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + var data = seriesModel.getData(); + data.each(coordSys.dimensions, function (x, y, idx) { + if (!isNaN(x) && !isNaN(y)) { + data.setItemLayout(idx, coordSys.dataToPoint([x, y])); + } + else { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout(idx, [NaN, NaN]); + } + }); + + simpleLayoutEdge(data.graph); + } + else if (!layout || layout === 'none') { + simpleLayoutHelper(seriesModel); + } + }); + }; + + +/***/ }, +/* 208 */ +/***/ function(module, exports, __webpack_require__) { + + + + var simpleLayoutEdge = __webpack_require__(209); + + module.exports = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + var graph = seriesModel.getGraph(); + + graph.eachNode(function (node) { + var model = node.getModel(); + node.setLayout([+model.get('x'), +model.get('y')]); + }); + + simpleLayoutEdge(graph); + }; + + +/***/ }, +/* 209 */ +/***/ function(module, exports, __webpack_require__) { + + + var vec2 = __webpack_require__(10); + module.exports = function (graph) { + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0; + var p1 = vec2.clone(edge.node1.getLayout()); + var p2 = vec2.clone(edge.node2.getLayout()); + var points = [p1, p2]; + if (+curveness) { + points.push([ + (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, + (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness + ]); + } + edge.setLayout(points); + }); + }; + + +/***/ }, +/* 210 */ +/***/ function(module, exports, __webpack_require__) { + + + var circularLayoutHelper = __webpack_require__(211); + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + if (seriesModel.get('layout') === 'circular') { + circularLayoutHelper(seriesModel); + } + }); + }; + + +/***/ }, +/* 211 */ +/***/ function(module, exports, __webpack_require__) { + + + var vec2 = __webpack_require__(10); + module.exports = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + + var rect = coordSys.getBoundingRect(); + + var nodeData = seriesModel.getData(); + var graph = nodeData.graph; + + var angle = 0; + var sum = nodeData.getSum('value'); + var unitAngle = Math.PI * 2 / (sum || nodeData.count()); + + var cx = rect.width / 2 + rect.x; + var cy = rect.height / 2 + rect.y; + + var r = Math.min(rect.width, rect.height) / 2; + + graph.eachNode(function (node) { + var value = node.getValue('value'); + + angle += unitAngle * (sum ? value : 2) / 2; + + node.setLayout([ + r * Math.cos(angle) + cx, + r * Math.sin(angle) + cy + ]); + + angle += unitAngle * (sum ? value : 2) / 2; + }); + + nodeData.setLayout({ + cx: cx, + cy: cy + }); + + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0; + var p1 = vec2.clone(edge.node1.getLayout()); + var p2 = vec2.clone(edge.node2.getLayout()); + var cp1; + var x12 = (p1[0] + p2[0]) / 2; + var y12 = (p1[1] + p2[1]) / 2; + if (+curveness) { + curveness *= 3; + cp1 = [ + cx * curveness + x12 * (1 - curveness), + cy * curveness + y12 * (1 - curveness) + ]; + } + edge.setLayout([p1, p2, cp1]); + }); + }; + + +/***/ }, +/* 212 */ +/***/ function(module, exports, __webpack_require__) { + + + + var forceHelper = __webpack_require__(213); + var numberUtil = __webpack_require__(7); + var simpleLayoutHelper = __webpack_require__(208); + var circularLayoutHelper = __webpack_require__(211); + var vec2 = __webpack_require__(10); + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (graphSeries) { + var coordSys = graphSeries.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + if (graphSeries.get('layout') === 'force') { + var preservedPoints = graphSeries.preservedPoints || {}; + var graph = graphSeries.getGraph(); + var nodeData = graph.data; + var edgeData = graph.edgeData; + var forceModel = graphSeries.getModel('force'); + var initLayout = forceModel.get('initLayout'); + if (graphSeries.preservedPoints) { + nodeData.each(function (idx) { + var id = nodeData.getId(idx); + nodeData.setItemLayout(idx, preservedPoints[id] || [NaN, NaN]); + }); + } + else if (!initLayout || initLayout === 'none') { + simpleLayoutHelper(graphSeries); + } + else if (initLayout === 'circular') { + circularLayoutHelper(graphSeries); + } + + var nodeDataExtent = nodeData.getDataExtent('value'); + var edgeDataExtent = edgeData.getDataExtent('value'); + // var edgeDataExtent = edgeData.getDataExtent('value'); + var repulsion = forceModel.get('repulsion'); + var edgeLength = forceModel.get('edgeLength'); + if (!zrUtil.isArray(repulsion)) { + repulsion = [repulsion, repulsion]; + } + if (!zrUtil.isArray(edgeLength)) { + edgeLength = [edgeLength, edgeLength]; + } + // Larger value has smaller length + edgeLength = [edgeLength[1], edgeLength[0]]; + + var nodes = nodeData.mapArray('value', function (value, idx) { + var point = nodeData.getItemLayout(idx); + // var w = numberUtil.linearMap(value, nodeDataExtent, [0, 50]); + var rep = numberUtil.linearMap(value, nodeDataExtent, repulsion); + if (isNaN(rep)) { + rep = (repulsion[0] + repulsion[1]) / 2; + } + return { + w: rep, + rep: rep, + p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point + }; + }); + var edges = edgeData.mapArray('value', function (value, idx) { + var edge = graph.getEdgeByIndex(idx); + var d = numberUtil.linearMap(value, edgeDataExtent, edgeLength); + if (isNaN(d)) { + d = (edgeLength[0] + edgeLength[1]) / 2; + } + return { + n1: nodes[edge.node1.dataIndex], + n2: nodes[edge.node2.dataIndex], + d: d, + curveness: edge.getModel().get('lineStyle.normal.curveness') || 0 + }; + }); + + var coordSys = graphSeries.coordinateSystem; + var rect = coordSys.getBoundingRect(); + var forceInstance = forceHelper(nodes, edges, { + rect: rect, + gravity: forceModel.get('gravity') + }); + var oldStep = forceInstance.step; + forceInstance.step = function (cb) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i].fixed) { + // Write back to layout instance + vec2.copy(nodes[i].p, graph.getNodeByIndex(i).getLayout()); + } + } + oldStep(function (nodes, edges, stopped) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].fixed) { + graph.getNodeByIndex(i).setLayout(nodes[i].p); + } + preservedPoints[nodeData.getId(i)] = nodes[i].p; + } + for (var i = 0, l = edges.length; i < l; i++) { + var e = edges[i]; + var edge = graph.getEdgeByIndex(i); + var p1 = e.n1.p; + var p2 = e.n2.p; + var points = edge.getLayout(); + points = points ? points.slice() : []; + points[0] = points[0] || []; + points[1] = points[1] || []; + vec2.copy(points[0], p1); + vec2.copy(points[1], p2); + if (+e.curveness) { + points[2] = [ + (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, + (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness + ]; + } + edge.setLayout(points); + } + // Update layout + + cb && cb(stopped); + }); + }; + graphSeries.forceLayout = forceInstance; + graphSeries.preservedPoints = preservedPoints; + + // Step to get the layout + forceInstance.step(); + } + else { + // Remove prev injected forceLayout instance + graphSeries.forceLayout = null; + } + }); + }; + + +/***/ }, +/* 213 */ +/***/ function(module, exports, __webpack_require__) { + + + + var vec2 = __webpack_require__(10); + var scaleAndAdd = vec2.scaleAndAdd; + + // function adjacentNode(n, e) { + // return e.n1 === n ? e.n2 : e.n1; + // } + + module.exports = function (nodes, edges, opts) { + var rect = opts.rect; + var width = rect.width; + var height = rect.height; + var center = [rect.x + width / 2, rect.y + height / 2]; + // var scale = opts.scale || 1; + var gravity = opts.gravity == null ? 0.1 : opts.gravity; + + // for (var i = 0; i < edges.length; i++) { + // var e = edges[i]; + // var n1 = e.n1; + // var n2 = e.n2; + // n1.edges = n1.edges || []; + // n2.edges = n2.edges || []; + // n1.edges.push(e); + // n2.edges.push(e); + // } + // Init position + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + if (!n.p) { + // Use the position from first adjecent node with defined position + // Or use a random position + // From d3 + // if (n.edges) { + // var j = -1; + // while (++j < n.edges.length) { + // var e = n.edges[j]; + // var other = adjacentNode(n, e); + // if (other.p) { + // n.p = vec2.clone(other.p); + // break; + // } + // } + // } + // if (!n.p) { + n.p = vec2.create( + width * (Math.random() - 0.5) + center[0], + height * (Math.random() - 0.5) + center[1] + ); + // } + } + n.pp = vec2.clone(n.p); + n.edges = null; + } + + // Formula in 'Graph Drawing by Force-directed Placement' + // var k = scale * Math.sqrt(width * height / nodes.length); + // var k2 = k * k; + + var friction = 0.6; + + return { + warmUp: function () { + friction = 0.5; + }, + + setFixed: function (idx) { + nodes[idx].fixed = true; + }, + + setUnfixed: function (idx) { + nodes[idx].fixed = false; + }, + + step: function (cb) { + var v12 = []; + var nLen = nodes.length; + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + var n1 = e.n1; + var n2 = e.n2; + + vec2.sub(v12, n2.p, n1.p); + var d = vec2.len(v12) - e.d; + var w = n2.w / (n1.w + n2.w); + vec2.normalize(v12, v12); + + !n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction); + !n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction); + } + // Gravity + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + vec2.sub(v12, center, n.p); + // var d = vec2.len(v12); + // vec2.scale(v12, v12, 1 / d); + // var gravityFactor = gravity; + vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction); + } + } + + // Repulsive + // PENDING + for (var i = 0; i < nLen; i++) { + var n1 = nodes[i]; + for (var j = i + 1; j < nLen; j++) { + var n2 = nodes[j]; + vec2.sub(v12, n2.p, n1.p); + var d = vec2.len(v12); + if (d === 0) { + // Random repulse + vec2.set(v12, Math.random() - 0.5, Math.random() - 0.5); + d = 1; + } + var repFact = (n1.rep + n2.rep) / d / d; + !n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact); + !n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact); + } + } + var v = []; + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + vec2.sub(v, n.p, n.pp); + vec2.scaleAndAdd(n.p, n.p, v, friction); + vec2.copy(n.pp, n.p); + } + } + + friction = friction * 0.992; + + cb && cb(nodes, edges, friction < 0.01); + } + }; + }; + + +/***/ }, +/* 214 */ +/***/ function(module, exports, __webpack_require__) { + + + // FIXME Where to create the simple view coordinate system + var View = __webpack_require__(168); + var layout = __webpack_require__(21); + var bbox = __webpack_require__(51); + + function getViewRect(seriesModel, api, aspect) { + var option = seriesModel.getBoxLayoutParams(); + option.aspect = aspect; + return layout.getLayoutRect(option, { + width: api.getWidth(), + height: api.getHeight() + }); + } + + module.exports = function (ecModel, api) { + var viewList = []; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var coordSysType = seriesModel.get('coordinateSystem'); + if (!coordSysType || coordSysType === 'view') { + var viewCoordSys = new View(); + viewList.push(viewCoordSys); + + var data = seriesModel.getData(); + var positions = data.mapArray(function (idx) { + var itemModel = data.getItemModel(idx); + return [+itemModel.get('x'), +itemModel.get('y')]; + }); + + var min = []; + var max = []; + + bbox.fromPoints(positions, min, max); + + // If width or height is 0 + if (max[0] - min[0] === 0) { + max[0] += 1; + min[0] -= 1; + } + if (max[1] - min[1] === 0) { + max[1] += 1; + min[1] -= 1; + } + var aspect = (max[0] - min[0]) / (max[1] - min[1]); + // FIXME If get view rect after data processed? + var viewRect = getViewRect(seriesModel, api, aspect); + // Position may be NaN, use view rect instead + if (isNaN(aspect)) { + min = [viewRect.x, viewRect.y]; + max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height]; + } + + var bbWidth = max[0] - min[0]; + var bbHeight = max[1] - min[1]; + + var viewWidth = viewRect.width; + var viewHeight = viewRect.height; + + viewCoordSys = seriesModel.coordinateSystem = new View(); + viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); + + viewCoordSys.setBoundingRect( + min[0], min[1], bbWidth, bbHeight + ); + viewCoordSys.setViewRect( + viewRect.x, viewRect.y, viewWidth, viewHeight + ); + + // Update roam info + viewCoordSys.setCenter(seriesModel.get('center')); + viewCoordSys.setZoom(seriesModel.get('zoom')); + } + }); + return viewList; + }; + + +/***/ }, +/* 215 */ +/***/ function(module, exports, __webpack_require__) { + + + __webpack_require__(216); + __webpack_require__(217); + + +/***/ }, +/* 216 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var SeriesModel = __webpack_require__(28); + var zrUtil = __webpack_require__(4); + + var GaugeSeries = SeriesModel.extend({ + + type: 'series.gauge', + + getInitialData: function (option, ecModel) { + var list = new List(['value'], this); + var dataOpt = option.data || []; + if (!zrUtil.isArray(dataOpt)) { + dataOpt = [dataOpt]; + } + // Only use the first data item + list.initData(dataOpt); + return list; + }, + + defaultOption: { + zlevel: 0, + z: 2, + // 默认全局居中 + center: ['50%', '50%'], + legendHoverLink: true, + radius: '75%', + startAngle: 225, + endAngle: -45, + clockwise: true, + // 最小值 + min: 0, + // 最大值 + max: 100, + // 分割段数,默认为10 + splitNumber: 10, + // 坐标轴线 + axisLine: { + // 默认显示,属性show控制显示与否 + show: true, + lineStyle: { // 属性lineStyle控制线条样式 + color: [[0.2, '#91c7ae'], [0.8, '#63869e'], [1, '#c23531']], + width: 30 + } + }, + // 分隔线 + splitLine: { + // 默认显示,属性show控制显示与否 + show: true, + // 属性length控制线长 + length: 30, + // 属性lineStyle(详见lineStyle)控制线条样式 + lineStyle: { + color: '#eee', + width: 2, + type: 'solid' + } + }, + // 坐标轴小标记 + axisTick: { + // 属性show控制显示与否,默认不显示 + show: true, + // 每份split细分多少段 + splitNumber: 5, + // 属性length控制线长 + length: 8, + // 属性lineStyle控制线条样式 + lineStyle: { + color: '#eee', + width: 1, + type: 'solid' + } + }, + axisLabel: { + show: true, + distance: 5, + // formatter: null, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: 'auto' + } + }, + pointer: { + show: true, + length: '80%', + width: 8 + }, + itemStyle: { + normal: { + color: 'auto' + } + }, + title: { + show: true, + // x, y,单位px + offsetCenter: [0, '-40%'], + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + color: '#333', + fontSize: 15 + } + }, + detail: { + show: true, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 0, + borderColor: '#ccc', + width: 100, + height: 40, + // x, y,单位px + offsetCenter: [0, '40%'], + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + color: 'auto', + fontSize: 30 + } + } + } + }); + + module.exports = GaugeSeries; + + +/***/ }, +/* 217 */ +/***/ function(module, exports, __webpack_require__) { + + + + var PointerPath = __webpack_require__(218); + + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + + function parsePosition(seriesModel, api) { + var center = seriesModel.get('center'); + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent(center[0], api.getWidth()); + var cy = parsePercent(center[1], api.getHeight()); + var r = parsePercent(seriesModel.get('radius'), size / 2); + + return { + cx: cx, + cy: cy, + r: r + }; + } + + function formatLabel(label, labelFormatter) { + if (labelFormatter) { + if (typeof labelFormatter === 'string') { + label = labelFormatter.replace('{value}', label); + } + else if (typeof labelFormatter === 'function') { + label = labelFormatter(label); + } + } + + return label; + } + + var PI2 = Math.PI * 2; + + var GaugeView = __webpack_require__(42).extend({ + + type: 'gauge', + + render: function (seriesModel, ecModel, api) { + + this.group.removeAll(); + + var colorList = seriesModel.get('axisLine.lineStyle.color'); + var posInfo = parsePosition(seriesModel, api); + + this._renderMain( + seriesModel, ecModel, api, colorList, posInfo + ); + }, + + _renderMain: function (seriesModel, ecModel, api, colorList, posInfo) { + var group = this.group; + + var axisLineModel = seriesModel.getModel('axisLine'); + var lineStyleModel = axisLineModel.getModel('lineStyle'); + + var clockwise = seriesModel.get('clockwise'); + var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI; + var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI; + + var angleRangeSpan = (endAngle - startAngle) % PI2; + + var prevEndAngle = startAngle; + var axisLineWidth = lineStyleModel.get('width'); + + for (var i = 0; i < colorList.length; i++) { + // Clamp + var percent = Math.min(Math.max(colorList[i][0], 0), 1); + var endAngle = startAngle + angleRangeSpan * percent; + var sector = new graphic.Sector({ + shape: { + startAngle: prevEndAngle, + endAngle: endAngle, + cx: posInfo.cx, + cy: posInfo.cy, + clockwise: clockwise, + r0: posInfo.r - axisLineWidth, + r: posInfo.r + }, + silent: true + }); + + sector.setStyle({ + fill: colorList[i][1] + }); + + sector.setStyle(lineStyleModel.getLineStyle( + // Because we use sector to simulate arc + // so the properties for stroking are useless + ['color', 'borderWidth', 'borderColor'] + )); + + group.add(sector); + + prevEndAngle = endAngle; + } + + var getColor = function (percent) { + // Less than 0 + if (percent <= 0) { + return colorList[0][1]; + } + for (var i = 0; i < colorList.length; i++) { + if (colorList[i][0] >= percent + && (i === 0 ? 0 : colorList[i - 1][0]) < percent + ) { + return colorList[i][1]; + } + } + // More than 1 + return colorList[i - 1][1]; + }; + + if (!clockwise) { + var tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + this._renderTicks( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ); + + this._renderPointer( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ); + + this._renderTitle( + seriesModel, ecModel, api, getColor, posInfo + ); + this._renderDetail( + seriesModel, ecModel, api, getColor, posInfo + ); + }, + + _renderTicks: function ( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ) { + var group = this.group; + var cx = posInfo.cx; + var cy = posInfo.cy; + var r = posInfo.r; + + var minVal = seriesModel.get('min'); + var maxVal = seriesModel.get('max'); + + var splitLineModel = seriesModel.getModel('splitLine'); + var tickModel = seriesModel.getModel('axisTick'); + var labelModel = seriesModel.getModel('axisLabel'); + + var splitNumber = seriesModel.get('splitNumber'); + var subSplitNumber = tickModel.get('splitNumber'); + + var splitLineLen = parsePercent( + splitLineModel.get('length'), r + ); + var tickLen = parsePercent( + tickModel.get('length'), r + ); + + var angle = startAngle; + var step = (endAngle - startAngle) / splitNumber; + var subStep = step / subSplitNumber; + + var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle(); + var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle(); + var textStyleModel = labelModel.getModel('textStyle'); + + for (var i = 0; i <= splitNumber; i++) { + var unitX = Math.cos(angle); + var unitY = Math.sin(angle); + // Split line + if (splitLineModel.get('show')) { + var splitLine = new graphic.Line({ + shape: { + x1: unitX * r + cx, + y1: unitY * r + cy, + x2: unitX * (r - splitLineLen) + cx, + y2: unitY * (r - splitLineLen) + cy + }, + style: splitLineStyle, + silent: true + }); + if (splitLineStyle.stroke === 'auto') { + splitLine.setStyle({ + stroke: getColor(i / splitNumber) + }); + } + + group.add(splitLine); + } + + // Label + if (labelModel.get('show')) { + var label = formatLabel( + numberUtil.round(i / splitNumber * (maxVal - minVal) + minVal), + labelModel.get('formatter') + ); + var distance = labelModel.get('distance'); + + var text = new graphic.Text({ + style: { + text: label, + x: unitX * (r - splitLineLen - distance) + cx, + y: unitY * (r - splitLineLen - distance) + cy, + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'), + textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center') + }, + silent: true + }); + if (text.style.fill === 'auto') { + text.setStyle({ + fill: getColor(i / splitNumber) + }); + } + + group.add(text); + } + + // Axis tick + if (tickModel.get('show') && i !== splitNumber) { + for (var j = 0; j <= subSplitNumber; j++) { + var unitX = Math.cos(angle); + var unitY = Math.sin(angle); + var tickLine = new graphic.Line({ + shape: { + x1: unitX * r + cx, + y1: unitY * r + cy, + x2: unitX * (r - tickLen) + cx, + y2: unitY * (r - tickLen) + cy + }, + silent: true, + style: tickLineStyle + }); + + if (tickLineStyle.stroke === 'auto') { + tickLine.setStyle({ + stroke: getColor((i + j / subSplitNumber) / splitNumber) + }); + } + + group.add(tickLine); + angle += subStep; + } + angle -= subStep; + } + else { + angle += step; + } + } + }, + + _renderPointer: function ( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ) { + var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')]; + var angleExtent = [startAngle, endAngle]; + + if (!clockwise) { + angleExtent = angleExtent.reverse(); + } + + var data = seriesModel.getData(); + var oldData = this._data; + + var group = this.group; + + data.diff(oldData) + .add(function (idx) { + var pointer = new PointerPath({ + shape: { + angle: startAngle + } + }); + + graphic.updateProps(pointer, { + shape: { + angle: numberUtil.linearMap(data.get('value', idx), valueExtent, angleExtent, true) + } + }, seriesModel); + + group.add(pointer); + data.setItemGraphicEl(idx, pointer); + }) + .update(function (newIdx, oldIdx) { + var pointer = oldData.getItemGraphicEl(oldIdx); + + graphic.updateProps(pointer, { + shape: { + angle: numberUtil.linearMap(data.get('value', newIdx), valueExtent, angleExtent, true) + } + }, seriesModel); + + group.add(pointer); + data.setItemGraphicEl(newIdx, pointer); + }) + .remove(function (idx) { + var pointer = oldData.getItemGraphicEl(idx); + group.remove(pointer); + }) + .execute(); + + data.eachItemGraphicEl(function (pointer, idx) { + var itemModel = data.getItemModel(idx); + var pointerModel = itemModel.getModel('pointer'); + + pointer.setShape({ + x: posInfo.cx, + y: posInfo.cy, + width: parsePercent( + pointerModel.get('width'), posInfo.r + ), + r: parsePercent(pointerModel.get('length'), posInfo.r) + }); + + pointer.useStyle(itemModel.getModel('itemStyle.normal').getItemStyle()); + + if (pointer.style.fill === 'auto') { + pointer.setStyle('fill', getColor( + (data.get('value', idx) - valueExtent[0]) / (valueExtent[1] - valueExtent[0]) + )); + } + + graphic.setHoverStyle( + pointer, itemModel.getModel('itemStyle.emphasis').getItemStyle() + ); + }); + + this._data = data; + }, + + _renderTitle: function ( + seriesModel, ecModel, api, getColor, posInfo + ) { + var titleModel = seriesModel.getModel('title'); + if (titleModel.get('show')) { + var textStyleModel = titleModel.getModel('textStyle'); + var offsetCenter = titleModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r); + var text = new graphic.Text({ + style: { + x: x, + y: y, + // FIXME First data name ? + text: seriesModel.getData().getName(0), + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textAlign: 'center', + textVerticalAlign: 'middle' + } + }); + this.group.add(text); + } + }, + + _renderDetail: function ( + seriesModel, ecModel, api, getColor, posInfo + ) { + var detailModel = seriesModel.getModel('detail'); + var minVal = seriesModel.get('min'); + var maxVal = seriesModel.get('max'); + if (detailModel.get('show')) { + var textStyleModel = detailModel.getModel('textStyle'); + var offsetCenter = detailModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r); + var width = parsePercent(detailModel.get('width'), posInfo.r); + var height = parsePercent(detailModel.get('height'), posInfo.r); + var value = seriesModel.getData().get('value', 0); + var rect = new graphic.Rect({ + shape: { + x: x - width / 2, + y: y - height / 2, + width: width, + height: height + }, + style: { + text: formatLabel( + // FIXME First data name ? + value, detailModel.get('formatter') + ), + fill: detailModel.get('backgroundColor'), + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + } + }); + if (rect.style.textFill === 'auto') { + rect.setStyle('textFill', getColor( + numberUtil.linearMap(value, [minVal, maxVal], [0, 1], true) + )); + } + rect.setStyle(detailModel.getItemStyle(['color'])); + this.group.add(rect); + } + } + }); + + module.exports = GaugeView; + + +/***/ }, +/* 218 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(45).extend({ + + type: 'echartsGaugePointer', + + shape: { + angle: 0, + + width: 10, + + r: 10, + + x: 0, + + y: 0 + }, + + buildPath: function (ctx, shape) { + var mathCos = Math.cos; + var mathSin = Math.sin; + + var r = shape.r; + var width = shape.width; + var angle = shape.angle; + var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2); + var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2); + + angle = shape.angle - Math.PI / 2; + ctx.moveTo(x, y); + ctx.lineTo( + shape.x + mathCos(angle) * width, + shape.y + mathSin(angle) * width + ); + ctx.lineTo( + shape.x + mathCos(shape.angle) * r, + shape.y + mathSin(shape.angle) * r + ); + ctx.lineTo( + shape.x - mathCos(angle) * width, + shape.y - mathSin(angle) * width + ); + ctx.lineTo(x, y); + return; + } + }); + + +/***/ }, +/* 219 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(220); + __webpack_require__(221); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'funnel')); + echarts.registerLayout(__webpack_require__(222)); + + echarts.registerProcessor(zrUtil.curry(__webpack_require__(146), 'funnel')); + + +/***/ }, +/* 220 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var modelUtil = __webpack_require__(5); + var completeDimensions = __webpack_require__(102); + + var FunnelSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.funnel', + + init: function (option) { + FunnelSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + // Extend labelLine emphasis + this._defaultLabelLine(option); + }, + + getInitialData: function (option, ecModel) { + var dimensions = completeDimensions(['value'], option.data); + var list = new List(dimensions, this); + list.initData(option.data); + return list; + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + modelUtil.defaultEmphasis(option.labelLine, ['show']); + + var labelLineNormalOpt = option.labelLine.normal; + var labelLineEmphasisOpt = option.labelLine.emphasis; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.normal.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.label.emphasis.show; + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + legendHoverLink: true, + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + + // 默认取数据最小最大值 + // min: 0, + // max: 100, + minSize: '0%', + maxSize: '100%', + sort: 'descending', // 'ascending', 'descending' + gap: 0, + funnelAlign: 'center', + label: { + normal: { + show: true, + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + }, + emphasis: { + show: true + } + }, + labelLine: { + normal: { + show: true, + length: 20, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + }, + emphasis: {} + }, + itemStyle: { + normal: { + // color: 各异, + borderColor: '#fff', + borderWidth: 1 + }, + emphasis: { + // color: 各异, + } + } + } + }); + + module.exports = FunnelSeries; + + +/***/ }, +/* 221 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * Piece of pie including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ + function FunnelPiece(data, idx) { + + graphic.Group.call(this); + + var polygon = new graphic.Polygon(); + var labelLine = new graphic.Polyline(); + var text = new graphic.Text(); + this.add(polygon); + this.add(labelLine); + this.add(text); + + this.updateData(data, idx, true); + + // Hover to change label and labelLine + function onEmphasis() { + labelLine.ignore = labelLine.hoverIgnore; + text.ignore = text.hoverIgnore; + } + function onNormal() { + labelLine.ignore = labelLine.normalIgnore; + text.ignore = text.normalIgnore; + } + this.on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('mouseover', onEmphasis) + .on('mouseout', onNormal); + } + + var funnelPieceProto = FunnelPiece.prototype; + + function getLabelStyle(data, idx, state, labelModel) { + var textStyleModel = labelModel.getModel('textStyle'); + var position = labelModel.get('position'); + var isLabelInside = position === 'inside' || position === 'inner' || position === 'center'; + return { + fill: textStyleModel.getTextColor() + || (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')), + textFont: textStyleModel.getFont(), + text: zrUtil.retrieve( + data.hostModel.getFormattedLabel(idx, state), + data.getName(idx) + ) + }; + } + + var opacityAccessPath = ['itemStyle', 'normal', 'opacity']; + funnelPieceProto.updateData = function (data, idx, firstCreate) { + + var polygon = this.childAt(0); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var opacity = data.getItemModel(idx).get(opacityAccessPath); + opacity = opacity == null ? 1 : opacity; + + // Reset style + polygon.useStyle({}); + + if (firstCreate) { + polygon.setShape({ + points: layout.points + }); + polygon.setStyle({ opacity : 0 }); + graphic.initProps(polygon, { + style: { + opacity: opacity + } + }, seriesModel, idx); + } + else { + graphic.updateProps(polygon, { + style: { + opacity: opacity + }, + shape: { + points: layout.points + } + }, seriesModel, idx); + } + + // Update common style + var itemStyleModel = itemModel.getModel('itemStyle'); + var visualColor = data.getItemVisual(idx, 'color'); + + polygon.setStyle( + zrUtil.defaults( + { + lineJoin: 'round', + fill: visualColor + }, + itemStyleModel.getModel('normal').getItemStyle(['opacity']) + ) + ); + polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); + + this._updateLabel(data, idx); + + graphic.setHoverStyle(this); + }; + + funnelPieceProto._updateLabel = function (data, idx) { + + var labelLine = this.childAt(1); + var labelText = this.childAt(2); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var labelLayout = layout.label; + var visualColor = data.getItemVisual(idx, 'color'); + + graphic.updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || labelLayout.linePoints + } + }, seriesModel, idx); + + graphic.updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + style: { + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign, + textFont: labelLayout.font + }, + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineHoverModel = itemModel.getModel('labelLine.emphasis'); + + labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel)); + + labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); + labelText.hoverIgnore = !labelHoverModel.get('show'); + + labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); + labelLine.hoverIgnore = !labelLineHoverModel.get('show'); + + // Default use item visual color + labelLine.setStyle({ + stroke: visualColor + }); + labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); + + labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel); + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); + }; + + zrUtil.inherits(FunnelPiece, graphic.Group); + + + var Funnel = __webpack_require__(42).extend({ + + type: 'funnel', + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var oldData = this._data; + + var group = this.group; + + data.diff(oldData) + .add(function (idx) { + var funnelPiece = new FunnelPiece(data, idx); + + data.setItemGraphicEl(idx, funnelPiece); + + group.add(funnelPiece); + }) + .update(function (newIdx, oldIdx) { + var piePiece = oldData.getItemGraphicEl(oldIdx); + + piePiece.updateData(data, newIdx); + + group.add(piePiece); + data.setItemGraphicEl(newIdx, piePiece); + }) + .remove(function (idx) { + var piePiece = oldData.getItemGraphicEl(idx); + group.remove(piePiece); + }) + .execute(); + + this._data = data; + }, + + remove: function () { + this.group.removeAll(); + this._data = null; + } + }); + + module.exports = Funnel; + + +/***/ }, +/* 222 */ +/***/ function(module, exports, __webpack_require__) { + + + + var layout = __webpack_require__(21); + var number = __webpack_require__(7); + + var parsePercent = number.parsePercent; + + function getViewRect(seriesModel, api) { + return layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); + } + + function getSortedIndices(data, sort) { + var valueArr = data.mapArray('value', function (val) { + return val; + }); + var indices = []; + var isAscending = sort === 'ascending'; + for (var i = 0, len = data.count(); i < len; i++) { + indices[i] = i; + } + indices.sort(function (a, b) { + return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a]; + }); + return indices; + } + + function labelLayout (data) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var labelPosition = labelModel.get('position'); + + var labelLineModel = itemModel.getModel('labelLine.normal'); + + var layout = data.getItemLayout(idx); + var points = layout.points; + + var isLabelInside = labelPosition === 'inner' + || labelPosition === 'inside' || labelPosition === 'center'; + + var textAlign; + var textX; + var textY; + var linePoints; + + if (isLabelInside) { + textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4; + textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4; + textAlign = 'center'; + linePoints = [ + [textX, textY], [textX, textY] + ]; + } + else { + var x1; + var y1; + var x2; + var labelLineLen = labelLineModel.get('length'); + if (labelPosition === 'left') { + // Left side + x1 = (points[3][0] + points[0][0]) / 2; + y1 = (points[3][1] + points[0][1]) / 2; + x2 = x1 - labelLineLen; + textX = x2 - 5; + textAlign = 'right'; + } + else { + // Right side + x1 = (points[1][0] + points[2][0]) / 2; + y1 = (points[1][1] + points[2][1]) / 2; + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'left'; + } + var y2 = y1; + + linePoints = [[x1, y1], [x2, y2]]; + textY = y2; + } + + layout.label = { + linePoints: linePoints, + x: textX, + y: textY, + verticalAlign: 'middle', + textAlign: textAlign, + inside: isLabelInside + }; + }); + } + + module.exports = function (ecModel, api, payload) { + ecModel.eachSeriesByType('funnel', function (seriesModel) { + var data = seriesModel.getData(); + var sort = seriesModel.get('sort'); + var viewRect = getViewRect(seriesModel, api); + var indices = getSortedIndices(data, sort); + + var sizeExtent = [ + parsePercent(seriesModel.get('minSize'), viewRect.width), + parsePercent(seriesModel.get('maxSize'), viewRect.width) + ]; + var dataExtent = data.getDataExtent('value'); + var min = seriesModel.get('min'); + var max = seriesModel.get('max'); + if (min == null) { + min = Math.min(dataExtent[0], 0); + } + if (max == null) { + max = dataExtent[1]; + } + + var funnelAlign = seriesModel.get('funnelAlign'); + var gap = seriesModel.get('gap'); + var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count(); + + var y = viewRect.y; + + var getLinePoints = function (idx, offY) { + // End point index is data.count() and we assign it 0 + var val = data.get('value', idx) || 0; + var itemWidth = number.linearMap(val, [min, max], sizeExtent, true); + var x0; + switch (funnelAlign) { + case 'left': + x0 = viewRect.x; + break; + case 'center': + x0 = viewRect.x + (viewRect.width - itemWidth) / 2; + break; + case 'right': + x0 = viewRect.x + viewRect.width - itemWidth; + break; + } + return [ + [x0, offY], + [x0 + itemWidth, offY] + ]; + }; + + if (sort === 'ascending') { + // From bottom to top + itemHeight = -itemHeight; + gap = -gap; + y += viewRect.height; + indices = indices.reverse(); + } + + for (var i = 0; i < indices.length; i++) { + var idx = indices[i]; + var nextIdx = indices[i + 1]; + var start = getLinePoints(idx, y); + var end = getLinePoints(nextIdx, y + itemHeight); + + y += itemHeight + gap; + + data.setItemLayout(idx, { + points: start.concat(end.slice().reverse()) + }); + } + + labelLayout(data); + }); + }; + + +/***/ }, +/* 223 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(224); + + __webpack_require__(235); + __webpack_require__(236); + + echarts.registerVisual(__webpack_require__(237)); + + + +/***/ }, +/* 224 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(225); + __webpack_require__(228); + __webpack_require__(230); + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + var CLICK_THRESHOLD = 5; // > 4 + + // Parallel view + echarts.extendComponentView({ + type: 'parallel', + + render: function (parallelModel, ecModel, api) { + var zr = api.getZr(); + + if (!this.__onMouseDown) { + // FIXME + // click: mousemove check. otherwise confilct with drag brush. + var mousedownPoint; + zr.on('mousedown', this.__onMouseDown = function (e) { + mousedownPoint = [e.offsetX, e.offsetY]; + }); + zr.on('mouseup', this.__onMouseUp = function (e) { + var point = [e.offsetX, e.offsetY]; + var dist = Math.pow(mousedownPoint[0] - point[0], 2) + + Math.pow(mousedownPoint[1] - point[1], 2); + + if (!parallelModel.get('axisExpandable') || dist > CLICK_THRESHOLD) { + return; + } + + var coordSys = parallelModel.coordinateSystem; + var closestDim = coordSys.findClosestAxisDim(point); + if (closestDim) { + var axisIndex = zrUtil.indexOf(coordSys.dimensions, closestDim); + api.dispatchAction({ + type: 'parallelAxisExpand', + axisExpandCenter: axisIndex + }); + } + }); + } + }, + + dispose: function (ecModel, api) { + api.getZr().off(this.__onMouseDown); + api.getZr().off(this.__onMouseUp); + } + }); + + echarts.registerPreprocessor( + __webpack_require__(234) + ); + + + +/***/ }, +/* 225 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parallel coordinate system creater. + */ + + + var Parallel = __webpack_require__(226); + + function create(ecModel, api) { + var coordSysList = []; + + ecModel.eachComponent('parallel', function (parallelModel, idx) { + var coordSys = new Parallel(parallelModel, ecModel, api); + + coordSys.name = 'parallel_' + idx; + coordSys.resize(parallelModel, api); + + parallelModel.coordinateSystem = coordSys; + coordSys.model = parallelModel; + + coordSysList.push(coordSys); + }); + + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'parallel') { + var parallelModel = ecModel.queryComponents({ + mainType: 'parallel', + index: seriesModel.get('parallelIndex'), + id: seriesModel.get('parallelId') + })[0]; + seriesModel.coordinateSystem = parallelModel.coordinateSystem; + } + }); + + return coordSysList; + } + + __webpack_require__(26).register('parallel', {create: create}); + + + +/***/ }, +/* 226 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parallel Coordinates + * + */ + + + var layout = __webpack_require__(21); + var axisHelper = __webpack_require__(114); + var zrUtil = __webpack_require__(4); + var ParallelAxis = __webpack_require__(227); + var graphic = __webpack_require__(43); + var matrix = __webpack_require__(11); + + var each = zrUtil.each; + + var PI = Math.PI; + + function Parallel(parallelModel, ecModel, api) { + + /** + * key: dimension + * @type {Object.} + * @private + */ + this._axesMap = {}; + + /** + * key: dimension + * value: {position: [], rotation, } + * @type {Object.} + * @private + */ + this._axesLayout = {}; + + /** + * Always follow axis order. + * @type {Array.} + * @readOnly + */ + this.dimensions = parallelModel.dimensions; + + /** + * @type {module:zrender/core/BoundingRect} + */ + this._rect; + + /** + * @type {module:echarts/coord/parallel/ParallelModel} + */ + this._model = parallelModel; + + this._init(parallelModel, ecModel, api); + } + + Parallel.prototype = { + + type: 'parallel', + + constructor: Parallel, + + /** + * Initialize cartesian coordinate systems + * @private + */ + _init: function (parallelModel, ecModel, api) { + + var dimensions = parallelModel.dimensions; + var parallelAxisIndex = parallelModel.parallelAxisIndex; + + each(dimensions, function (dim, idx) { + + var axisIndex = parallelAxisIndex[idx]; + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + + var axis = this._axesMap[dim] = new ParallelAxis( + dim, + axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisIndex + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + + // Inject axis into axisModel + axisModel.axis = axis; + + // Inject axisModel into axis + axis.model = axisModel; + }, this); + }, + + /** + * Update axis scale after data processed + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + update: function (ecModel, api) { + this._updateAxesFromSeries(this._model, ecModel); + }, + + /** + * Update properties from series + * @private + */ + _updateAxesFromSeries: function (parallelModel, ecModel) { + ecModel.eachSeries(function (seriesModel) { + + if (!parallelModel.contains(seriesModel, ecModel)) { + return; + } + + var data = seriesModel.getData(); + + each(this.dimensions, function (dim) { + var axis = this._axesMap[dim]; + axis.scale.unionExtent(data.getDataExtent(dim)); + axisHelper.niceScaleExtent(axis, axis.model); + }, this); + }, this); + }, + + /** + * Resize the parallel coordinate system. + * @param {module:echarts/coord/parallel/ParallelModel} parallelModel + * @param {module:echarts/ExtensionAPI} api + */ + resize: function (parallelModel, api) { + this._rect = layout.getLayoutRect( + parallelModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + this._layoutAxes(parallelModel); + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getRect: function () { + return this._rect; + }, + + /** + * @private + */ + _layoutAxes: function (parallelModel) { + var rect = this._rect; + var layout = parallelModel.get('layout'); + var axes = this._axesMap; + var dimensions = this.dimensions; + + var size = [rect.width, rect.height]; + var sizeIdx = layout === 'horizontal' ? 0 : 1; + var layoutLength = size[sizeIdx]; + var axisLength = size[1 - sizeIdx]; + var axisExtent = [0, axisLength]; + + each(axes, function (axis) { + var idx = axis.inverse ? 1 : 0; + axis.setExtent(axisExtent[idx], axisExtent[1 - idx]); + }); + + var axisExpandable = parallelModel.get('axisExpandable'); + var axisExpandWidth = parallelModel.get('axisExpandWidth'); + var axisExpandCenter = parallelModel.get('axisExpandCenter'); + var axisExpandCount = parallelModel.get('axisExpandCount') || 0; + var axisExpandWindow; + + if (axisExpandCenter != null) { + // Clamp + var left = Math.max(0, Math.floor(axisExpandCenter - (axisExpandCount - 1) / 2)); + var right = left + axisExpandCount - 1; + if (right >= dimensions.length) { + right = dimensions.length - 1; + left = Math.max(0, Math.floor(right - axisExpandCount + 1)); + } + axisExpandWindow = [left, right]; + } + + var calcPos = (axisExpandable && axisExpandWindow && axisExpandWidth) + ? function (axisIndex, layoutLength, axisCount) { + var peekIntervalCount = axisExpandWindow[1] - axisExpandWindow[0]; + var otherWidth = ( + layoutLength - axisExpandWidth * peekIntervalCount + ) / (axisCount - 1 - peekIntervalCount); + + var position; + + if (axisIndex < axisExpandWindow[0]) { + position = (axisIndex - 1) * otherWidth; + } + else if (axisIndex <= axisExpandWindow[1]) { + position = axisExpandWindow[0] * otherWidth + + (axisIndex - axisExpandWindow[0]) * axisExpandWidth; + } + else if (axisIndex === axisCount - 1) { + position = layoutLength; + } + else { + position = axisExpandWindow[0] * otherWidth + + peekIntervalCount * axisExpandWidth + + (axisIndex - axisExpandWindow[1]) * otherWidth; + } + + return { + position: position, + axisNameAvailableWidth: ( + axisExpandWindow[0] < axisIndex && axisIndex < axisExpandWindow[1] + ) ? axisExpandWidth : otherWidth + }; + } + : function (axisIndex, layoutLength, axisCount) { + var step = layoutLength / (axisCount - 1); + return { + position: step * axisIndex, + axisNameAvailableWidth: step + }; + }; + + each(dimensions, function (dim, idx) { + var posInfo = calcPos(idx, layoutLength, dimensions.length); + + var positionTable = { + horizontal: { + x: posInfo.position, + y: axisLength + }, + vertical: { + x: 0, + y: posInfo.position + } + }; + var rotationTable = { + horizontal: PI / 2, + vertical: 0 + }; + + var position = [ + positionTable[layout].x + rect.x, + positionTable[layout].y + rect.y + ]; + + var rotation = rotationTable[layout]; + var transform = matrix.create(); + matrix.rotate(transform, transform, rotation); + matrix.translate(transform, transform, position); + + // TODO + // tick等排布信息。 + + // TODO + // 根据axis order 更新 dimensions顺序。 + + this._axesLayout[dim] = { + position: position, + rotation: rotation, + transform: transform, + axisNameAvailableWidth: posInfo.axisNameAvailableWidth, + tickDirection: 1, + labelDirection: 1, + axisExpandWindow: axisExpandWindow + }; + }, this); + }, + + /** + * Get axis by dim. + * @param {string} dim + * @return {module:echarts/coord/parallel/ParallelAxis} [description] + */ + getAxis: function (dim) { + return this._axesMap[dim]; + }, + + /** + * Convert a dim value of a single item of series data to Point. + * @param {*} value + * @param {string} dim + * @return {Array} + */ + dataToPoint: function (value, dim) { + return this.axisCoordToPoint( + this._axesMap[dim].dataToCoord(value), + dim + ); + }, + + /** + * Travel data for one time, get activeState of each data item. + * @param {module:echarts/data/List} data + * @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal' + * {number} dataIndex + * @param {Object} context + */ + eachActiveState: function (data, callback, context) { + var dimensions = this.dimensions; + var axesMap = this._axesMap; + var hasActiveSet = this.hasAxisbrushed(); + + for (var i = 0, len = data.count(); i < len; i++) { + var values = data.getValues(dimensions, i); + var activeState; + + if (!hasActiveSet) { + activeState = 'normal'; + } + else { + activeState = 'active'; + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + var dimName = dimensions[j]; + var state = axesMap[dimName].model.getActiveState(values[j], j); + + if (state === 'inactive') { + activeState = 'inactive'; + break; + } + } + } + + callback.call(context, activeState, i); + } + }, + + /** + * Whether has any activeSet. + * @return {boolean} + */ + hasAxisbrushed: function () { + var dimensions = this.dimensions; + var axesMap = this._axesMap; + var hasActiveSet = false; + + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + if (axesMap[dimensions[j]].model.getActiveState() !== 'normal') { + hasActiveSet = true; + } + } + + return hasActiveSet; + }, + + /** + * Convert coords of each axis to Point. + * Return point. For example: [10, 20] + * @param {Array.} coords + * @param {string} dim + * @return {Array.} + */ + axisCoordToPoint: function (coord, dim) { + var axisLayout = this._axesLayout[dim]; + return graphic.applyTransform([coord, 0], axisLayout.transform); + }, + + /** + * Get axis layout. + */ + getAxisLayout: function (dim) { + return zrUtil.clone(this._axesLayout[dim]); + }, + + findClosestAxisDim: function (point) { + var axisDim; + var minDist = Infinity; + + zrUtil.each(this._axesLayout, function (axisLayout, dim) { + var localPoint = graphic.applyTransform(point, axisLayout.transform, true); + var extent = this._axesMap[dim].getExtent(); + + if (localPoint[0] < extent[0] || localPoint[0] > extent[1]) { + return; + } + + var dist = Math.abs(localPoint[1]); + if (dist < minDist) { + minDist = dist; + axisDim = dim; + } + }, this); + + return axisDim; + } + + }; + + module.exports = Parallel; + + +/***/ }, +/* 227 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + /** + * @constructor module:echarts/coord/parallel/ParallelAxis + * @extends {module:echarts/coord/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + */ + var ParallelAxis = function (dim, scale, coordExtent, axisType, axisIndex) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * @type {number} + * @readOnly + */ + this.axisIndex = axisIndex; + }; + + ParallelAxis.prototype = { + + constructor: ParallelAxis, + + /** + * Axis model + * @param {module:echarts/coord/parallel/AxisModel} + */ + model: null + + }; + + zrUtil.inherits(ParallelAxis, Axis); + + module.exports = ParallelAxis; + + +/***/ }, +/* 228 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Component = __webpack_require__(19); + + __webpack_require__(229); + + Component.extend({ + + type: 'parallel', + + dependencies: ['parallelAxis'], + + /** + * @type {module:echarts/coord/parallel/Parallel} + */ + coordinateSystem: null, + + /** + * Each item like: 'dim0', 'dim1', 'dim2', ... + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * Coresponding to dimensions. + * @type {Array.} + * @readOnly + */ + parallelAxisIndex: null, + + layoutMode: 'box', + + defaultOption: { + zlevel: 0, + z: 0, + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + + layout: 'horizontal', // 'horizontal' or 'vertical' + + // FIXME + // naming? + axisExpandable: false, + axisExpandCenter: null, + axisExpandCount: 0, + axisExpandWidth: 50, // FIXME '10%' ? + + parallelAxisDefault: null + }, + + /** + * @override + */ + init: function () { + Component.prototype.init.apply(this, arguments); + + this.mergeOption({}); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var thisOption = this.option; + + newOption && zrUtil.merge(thisOption, newOption, true); + + this._initDimensions(); + }, + + /** + * Whether series or axis is in this coordinate system. + * @param {module:echarts/model/Series|module:echarts/coord/parallel/AxisModel} model + * @param {module:echarts/model/Global} ecModel + */ + contains: function (model, ecModel) { + var parallelIndex = model.get('parallelIndex'); + return parallelIndex != null + && ecModel.getComponent('parallel', parallelIndex) === this; + }, + + setAxisExpand: function (opt) { + zrUtil.each( + ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth'], + function (name) { + if (opt.hasOwnProperty(name)) { + this.option[name] = opt[name]; + } + }, + this + ); + }, + + /** + * @private + */ + _initDimensions: function () { + var dimensions = this.dimensions = []; + var parallelAxisIndex = this.parallelAxisIndex = []; + + var axisModels = zrUtil.filter(this.dependentModels.parallelAxis, function (axisModel) { + // Can not use this.contains here, because + // initialization has not been completed yet. + return axisModel.get('parallelIndex') === this.componentIndex; + }); + + zrUtil.each(axisModels, function (axisModel) { + dimensions.push('dim' + axisModel.get('dim')); + parallelAxisIndex.push(axisModel.componentIndex); + }); + } + + }); + + + +/***/ }, +/* 229 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentModel = __webpack_require__(19); + var zrUtil = __webpack_require__(4); + var makeStyleMapper = __webpack_require__(15); + var axisModelCreator = __webpack_require__(127); + var numberUtil = __webpack_require__(7); + + var AxisModel = ComponentModel.extend({ + + type: 'baseParallelAxis', + + /** + * @type {module:echarts/coord/parallel/Axis} + */ + axis: null, + + /** + * @type {Array.} + * @readOnly + */ + activeIntervals: [], + + /** + * @return {Object} + */ + getAreaSelectStyle: function () { + return makeStyleMapper( + [ + ['fill', 'color'], + ['lineWidth', 'borderWidth'], + ['stroke', 'borderColor'], + ['width', 'width'], + ['opacity', 'opacity'] + ] + ).call(this.getModel('areaSelectStyle')); + }, + + /** + * The code of this feature is put on AxisModel but not ParallelAxis, + * because axisModel can be alive after echarts updating but instance of + * ParallelAxis having been disposed. this._activeInterval should be kept + * when action dispatched (i.e. legend click). + * + * @param {Array.>} intervals interval.length === 0 + * means set all active. + * @public + */ + setActiveIntervals: function (intervals) { + var activeIntervals = this.activeIntervals = zrUtil.clone(intervals); + + // Normalize + if (activeIntervals) { + for (var i = activeIntervals.length - 1; i >= 0; i--) { + numberUtil.asc(activeIntervals[i]); + } + } + }, + + /** + * @param {number|string} [value] When attempting to detect 'no activeIntervals set', + * value can not be input. + * @return {string} 'normal': no activeIntervals set, + * 'active', + * 'inactive'. + * @public + */ + getActiveState: function (value) { + var activeIntervals = this.activeIntervals; + + if (!activeIntervals.length) { + return 'normal'; + } + + if (value == null) { + return 'inactive'; + } + + for (var i = 0, len = activeIntervals.length; i < len; i++) { + if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) { + return 'active'; + } + } + return 'inactive'; + } + + }); + + var defaultOption = { + + type: 'value', + + /** + * @type {Array.} + */ + dim: null, // 0, 1, 2, ... + + // parallelIndex: null, + + areaSelectStyle: { + width: 20, + borderWidth: 1, + borderColor: 'rgba(160,197,232)', + color: 'rgba(160,197,232)', + opacity: 0.3 + }, + + realtime: true, // Whether realtime update view when select. + + z: 10 + }; + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + function getAxisType(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); + } + + axisModelCreator('parallel', AxisModel, getAxisType, defaultOption); + + module.exports = AxisModel; + + +/***/ }, +/* 230 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(225); + __webpack_require__(231); + __webpack_require__(232); + + + +/***/ }, +/* 231 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + /** + * @payload + * @property {string} parallelAxisId + * @property {Array.>} intervals + */ + var actionInfo = { + type: 'axisAreaSelect', + event: 'axisAreaSelected', + update: 'updateVisual' + }; + echarts.registerAction(actionInfo, function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallelAxis', query: payload}, + function (parallelAxisModel) { + parallelAxisModel.axis.model.setActiveIntervals(payload.intervals); + } + ); + }); + + /** + * @payload + */ + echarts.registerAction('parallelAxisExpand', function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallel', query: payload}, + function (parallelModel) { + parallelModel.setAxisExpand(payload); + } + ); + + }); + + +/***/ }, +/* 232 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var AxisBuilder = __webpack_require__(132); + var BrushController = __webpack_require__(233); + var graphic = __webpack_require__(43); + + var elementList = ['axisLine', 'axisLabel', 'axisTick', 'axisName']; + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'parallelAxis', + + /** + * @override + */ + init: function (ecModel, api) { + AxisView.superApply(this, 'init', arguments); + + /** + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)); + }, + + /** + * @override + */ + render: function (axisModel, ecModel, api, payload) { + if (fromAxisAreaSelect(axisModel, ecModel, payload)) { + return; + } + + this.axisModel = axisModel; + this.api = api; + + this.group.removeAll(); + + var oldAxisGroup = this._axisGroup; + this._axisGroup = new graphic.Group(); + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var coordSys = ecModel.getComponent( + 'parallel', axisModel.get('parallelIndex') + ).coordinateSystem; + + var areaSelectStyle = axisModel.getAreaSelectStyle(); + var areaWidth = areaSelectStyle.width; + + var dim = axisModel.axis.dim; + var axisLayout = coordSys.getAxisLayout(dim); + + // Fetch from axisModel by default. + var axisLabelShow; + var axisIndex = zrUtil.indexOf(coordSys.dimensions, dim); + + var axisExpandWindow = axisLayout.axisExpandWindow; + if (axisExpandWindow + && (axisIndex <= axisExpandWindow[0] || axisIndex >= axisExpandWindow[1]) + ) { + axisLabelShow = false; + } + + var builderOpt = zrUtil.extend( + { + axisLabelShow: axisLabelShow, + strokeContainThreshold: areaWidth + }, + axisLayout + ); + + var axisBuilder = new AxisBuilder(axisModel, builderOpt); + + zrUtil.each(elementList, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + this._refreshBrushController(builderOpt, areaSelectStyle, axisModel, areaWidth); + + graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel); + }, + + _refreshBrushController: function (builderOpt, areaSelectStyle, axisModel, areaWidth) { + // After filtering, axis may change, select area needs to be update. + var axis = axisModel.axis; + var coverInfoList = zrUtil.map(axisModel.activeIntervals, function (interval) { + return { + brushType: 'lineX', + panelId: 'pl', + range: [ + axis.dataToCoord(interval[0], true), + axis.dataToCoord(interval[1], true) + ] + }; + }); + + var extent = axis.getExtent(); + var extra = 30; // Arbitrary value. + var rect = { + x: extent[0] - extra, + y: -areaWidth / 2, + width: extent[1] - extent[0] + 2 * extra, + height: areaWidth + }; + + this._brushController + .mount({ + enableGlobalPan: true, + rotation: builderOpt.rotation, + position: builderOpt.position + }) + .setPanels([{ + panelId: 'pl', + rect: rect + }]) + .enableBrush({ + brushType: 'lineX', + brushStyle: areaSelectStyle, + removeOnClick: true + }) + .updateCovers(coverInfoList); + }, + + _onBrush: function (coverInfoList, opt) { + // Do not cache these object, because the mey be changed. + var axisModel = this.axisModel; + var axis = axisModel.axis; + + var intervals = zrUtil.map(coverInfoList, function (coverInfo) { + return [ + axis.coordToData(coverInfo.range[0], true), + axis.coordToData(coverInfo.range[1], true) + ]; + }); + + // If realtime is true, action is not dispatched on drag end, because + // the drag end emits the same params with the last drag move event, + // and may have some delay when using touch pad. + if (!axisModel.option.realtime === opt.isEnd || opt.removeOnClick) { // jshint ignore:line + this.api.dispatchAction({ + type: 'axisAreaSelect', + parallelAxisId: axisModel.id, + intervals: intervals + }); + } + }, + + /** + * @override + */ + dispose: function () { + this._brushController.dispose(); + } + }); + + function fromAxisAreaSelect(axisModel, ecModel, payload) { + return payload + && payload.type === 'axisAreaSelect' + && ecModel.findComponents( + {mainType: 'parallelAxis', query: payload} + )[0] === axisModel; + } + + module.exports = AxisView; + + +/***/ }, +/* 233 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Box selection tool. + * + * @module echarts/component/helper/BrushController + */ + + + + var Eventful = __webpack_require__(33); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var interactionMutex = __webpack_require__(175); + var DataDiffer = __webpack_require__(98); + + var curry = zrUtil.curry; + var each = zrUtil.each; + var map = zrUtil.map; + var mathMin = Math.min; + var mathMax = Math.max; + var mathPow = Math.pow; + + var COVER_Z = 10000; + var UNSELECT_THRESHOLD = 6; + var MIN_RESIZE_LINE_WIDTH = 6; + var MUTEX_RESOURCE_KEY = 'globalPan'; + + var DIRECTION_MAP = { + w: [0, 0], + e: [0, 1], + n: [1, 0], + s: [1, 1] + }; + var CURSOR_MAP = { + w: 'ew', + e: 'ew', + n: 'ns', + s: 'ns', + ne: 'nesw', + sw: 'nesw', + nw: 'nwse', + se: 'nwse' + }; + var DEFAULT_BRUSH_OPT = { + brushStyle: { + lineWidth: 2, + stroke: 'rgba(0,0,0,0.3)', + fill: 'rgba(0,0,0,0.1)' + }, + transformable: true, + brushMode: 'single', + removeOnClick: false + }; + + var baseUID = 0; + + /** + * @alias module:echarts/component/helper/BrushController + * @constructor + * @mixin {module:zrender/mixin/Eventful} + * @event module:echarts/component/helper/BrushController#brush + * params: + * areas: Array., coord relates to container group, + * If no container specified, to global. + * opt { + * isEnd: boolean, + * removeOnClick: boolean + * } + * + * @param {module:zrender/zrender~ZRender} zr + */ + function BrushController(zr) { + + if (true) { + zrUtil.assert(zr); + } + + Eventful.call(this); + + /** + * @type {module:zrender/zrender~ZRender} + * @private + */ + this._zr = zr; + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new graphic.Group(); + + /** + * Only for drawing (after enabledBrush). + * @private + * @type {string} + */ + this._brushType; + + /** + * Only for drawing (after enabledBrush). + * @private + * @type {Object} + */ + this._brushOption; + + /** + * @private + * @type {Object} + */ + this._panels; + + /** + * @private + * @type {Array.} + */ + this._track = []; + + /** + * @private + * @type {boolean} + */ + this._dragging; + + /** + * @private + * @type {Array} + */ + this._covers = []; + + /** + * @private + * @type {moudule:zrender/container/Group} + */ + this._creatingCover; + + /** + * true means global panel + * @private + * @type {module:zrender/container/Group|boolean} + */ + this._creatingPanel; + + /** + * @private + * @type {boolean} + */ + this._enableGlobalPan; + + /** + * @private + * @type {boolean} + */ + if (true) { + this._mounted; + } + + /** + * @private + * @type {string} + */ + this._uid = 'brushController_' + baseUID++; + + /** + * @private + * @type {Object} + */ + this._handlers = {}; + each(mouseHandlers, function (handler, eventName) { + this._handlers[eventName] = zrUtil.bind(handler, this); + }, this); + } + + BrushController.prototype = { + + constructor: BrushController, + + /** + * If set to null/undefined/false, select disabled. + * @param {Object} brushOption + * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false + * If pass false/null/undefined, disable brush. + * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple' + * @param {boolean} [brushOption.transformable=true] + * @param {boolean} [brushOption.removeOnClick=false] + * @param {Object} [brushOption.brushStyle] + * @param {number} [brushOption.brushStyle.width] + * @param {number} [brushOption.brushStyle.lineWidth] + * @param {string} [brushOption.brushStyle.stroke] + * @param {string} [brushOption.brushStyle.fill] + */ + enableBrush: function (brushOption) { + if (true) { + zrUtil.assert(this._mounted); + } + + this._brushType && doDisableBrush(this); + brushOption.brushType && doEnableBrush(this, brushOption); + + return this; + }, + + /** + * @param {Array.} panelOpts If not pass, it is global brush. + * Each items: {panelId, rect} + */ + setPanels: function (panelOpts) { + var oldPanels = this._panels || {}; + var newPanels = this._panels = panelOpts && panelOpts.length && {}; + var thisGroup = this.group; + + newPanels && each(panelOpts, function (panelOpt) { + var panelId = panelOpt.panelId; + var panel = oldPanels[panelId]; + if (!panel) { + panel = new graphic.Rect({ + silent: true, + invisible: true + }); + thisGroup.add(panel); + } + panel.attr('shape', panelOpt.rect); + panel.__brushPanelId = panelId; + newPanels[panelId] = panel; + oldPanels[panelId] = null; + }); + + each(oldPanels, function (panel) { + panel && thisGroup.remove(panel); + }); + + return this; + }, + + /** + * @param {Object} [opt] + * @return {boolean} [opt.enableGlobalPan=false] + * @return {boolean} [opt.position=[0, 0]] + * @return {boolean} [opt.rotation=0] + * @return {boolean} [opt.scale=[1, 1]] + */ + mount: function (opt) { + opt = opt || {}; + + if (true) { + this._mounted = true; // should be at first. + } + + this._enableGlobalPan = opt.enableGlobalPan; + + var thisGroup = this.group; + this._zr.add(thisGroup); + + thisGroup.attr({ + position: opt.position || [0, 0], + rotation: opt.rotation || 0, + scale: opt.scale || [1, 1] + }); + + return this; + }, + + eachCover: function (cb, context) { + each(this._covers, cb, context); + }, + + /** + * Update covers. + * @param {Array.} brushOptionList Like: + * [ + * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable}, + * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]}, + * ... + * ] + * `brushType` is required in each cover info. + * `id` is not mandatory. + * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default. + * If brushOptionList is null/undefined, all covers removed. + */ + updateCovers: function (brushOptionList) { + if (true) { + zrUtil.assert(this._mounted); + } + + brushOptionList = zrUtil.map(brushOptionList, function (brushOption) { + return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); + }); + + var tmpIdPrefix = '\0-brush-index-'; + var oldCovers = this._covers; + var newCovers = this._covers = []; + var controller = this; + var creatingCover = this._creatingCover; + + (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey)) + .add(addOrUpdate) + .update(addOrUpdate) + .remove(remove) + .execute(); + + return this; + + function getKey(brushOption, index) { + return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + + '-' + brushOption.brushType; + } + + function oldGetKey(cover, index) { + return getKey(cover.__brushOption, index); + } + + function addOrUpdate(newIndex, oldIndex) { + var newBrushOption = brushOptionList[newIndex]; + // Consider setOption in event listener of brushSelect, + // where updating cover when creating should be forbiden. + if (oldIndex != null && oldCovers[oldIndex] === creatingCover) { + newCovers[newIndex] = oldCovers[oldIndex]; + } + else { + var cover = newCovers[newIndex] = oldIndex != null + ? ( + oldCovers[oldIndex].__brushOption = newBrushOption, + oldCovers[oldIndex] + ) + : endCreating(controller, createCover(controller, newBrushOption)); + updateCoverAfterCreation(controller, cover); + } + } + + function remove(oldIndex) { + if (oldCovers[oldIndex] !== creatingCover) { + controller.group.remove(oldCovers[oldIndex]); + } + } + }, + + unmount: function () { + this.enableBrush(false); + + // container may 'removeAll' outside. + clearCovers(this); + this._zr.remove(this.group); + + if (true) { + this._mounted = false; // should be at last. + } + + return this; + }, + + dispose: function () { + this.unmount(); + this.off(); + } + }; + + zrUtil.mixin(BrushController, Eventful); + + + function doEnableBrush(controller, brushOption) { + var zr = controller._zr; + + // Consider roam, which takes globalPan too. + if (!controller._enableGlobalPan) { + interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid); + } + + each(controller._handlers, function (handler, eventName) { + zr.on(eventName, handler); + }); + + controller._brushType = brushOption.brushType; + controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); + } + + function doDisableBrush(controller) { + var zr = controller._zr; + + interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid); + + each(controller._handlers, function (handler, eventName) { + zr.off(eventName, handler); + }); + + controller._brushType = controller._brushOption = null; + } + + function createCover(controller, brushOption) { + var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption); + updateZ(cover); + cover.__brushOption = brushOption; + controller.group.add(cover); + return cover; + } + + function endCreating(controller, creatingCover) { + var coverRenderer = getCoverRenderer(creatingCover); + if (coverRenderer.endCreating) { + coverRenderer.endCreating(controller, creatingCover); + updateZ(creatingCover); + } + return creatingCover; + } + + function updateCoverShape(controller, cover) { + var brushOption = cover.__brushOption; + getCoverRenderer(cover).updateCoverShape( + controller, cover, brushOption.range, brushOption + ); + } + + function updateZ(group) { + group.traverse(function (el) { + el.z = COVER_Z; + el.z2 = COVER_Z; // Consider in given container. + }); + } + + function updateCoverAfterCreation(controller, cover) { + getCoverRenderer(cover).updateCommon(controller, cover); + updateCoverShape(controller, cover); + } + + function getCoverRenderer(cover) { + return coverRenderers[cover.__brushOption.brushType]; + } + + function getPanelByPoint(controller, x, y) { + var panels = controller._panels; + if (!panels) { + return true; // Global panel + } + var panel; + each(panels, function (pn) { + pn.contain(x, y) && (panel = pn); + }); + return panel; + } + + function getPanelByCover(controller, cover) { + var panels = controller._panels; + if (!panels) { + return true; // Global panel + } + var panelId = cover.__brushOption.panelId; + // User may give cover without coord sys info, + // which is then treated as global panel. + return panelId != null ? panels[panelId] : true; + } + + function clearCovers(controller) { + var covers = controller._covers; + var originalLength = covers.length; + each(covers, function (cover) { + controller.group.remove(cover); + }, controller); + covers.length = 0; + + return !!originalLength; + } + + function trigger(controller, opt) { + var areas = map(controller._covers, function (cover) { + var brushOption = cover.__brushOption; + var range = zrUtil.clone(brushOption.range); + + return { + brushType: brushOption.brushType, + panelId: brushOption.panelId, + range: range + }; + }); + + controller.trigger('brush', areas, { + isEnd: !!opt.isEnd, + removeOnClick: !!opt.removeOnClick + }); + } + + function shouldShowCover(controller) { + var track = controller._track; + + if (!track.length) { + return false; + } + + var p2 = track[track.length - 1]; + var p1 = track[0]; + var dx = p2[0] - p1[0]; + var dy = p2[1] - p1[1]; + var dist = mathPow(dx * dx + dy * dy, 0.5); + + return dist > UNSELECT_THRESHOLD; + } + + function getTrackEnds(track) { + var tail = track.length - 1; + tail < 0 && (tail = 0); + return [track[0], track[tail]]; + } + + function createBaseRectCover(doDrift, controller, brushOption, edgeNames) { + var cover = new graphic.Group(); + + cover.add(new graphic.Rect({ + name: 'main', + style: makeStyle(brushOption), + silent: true, + draggable: true, + cursor: 'move', + drift: curry(doDrift, controller, cover, 'nswe'), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + + each( + edgeNames, + function (name) { + cover.add(new graphic.Rect({ + name: name, + style: {opacity: 0}, + draggable: true, + silent: true, + invisible: true, + drift: curry(doDrift, controller, cover, name), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + } + ); + + return cover; + } + + function updateBaseRect(controller, cover, localRange, brushOption) { + var lineWidth = brushOption.brushStyle.lineWidth || 0; + var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH); + var x = localRange[0][0]; + var y = localRange[1][0]; + var xa = x - lineWidth / 2; + var ya = y - lineWidth / 2; + var x2 = localRange[0][1]; + var y2 = localRange[1][1]; + var x2a = x2 - handleSize + lineWidth / 2; + var y2a = y2 - handleSize + lineWidth / 2; + var width = x2 - x; + var height = y2 - y; + var widtha = width + lineWidth; + var heighta = height + lineWidth; + + updateRectShape(controller, cover, 'main', x, y, width, height); + + if (brushOption.transformable) { + updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta); + updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta); + updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize); + updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize); + + updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize); + updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize); + } + } + + function updateCommon(controller, cover) { + var brushOption = cover.__brushOption; + var transformable = brushOption.transformable; + + var mainEl = cover.childAt(0); + mainEl.useStyle(makeStyle(brushOption)); + mainEl.attr({ + silent: !transformable, + cursor: transformable ? 'move' : 'default' + }); + + each( + ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'], + function (name) { + var el = cover.childOfName(name); + var globalDir = getGlobalDirection(controller, name); + + el && el.attr({ + silent: !transformable, + invisible: !transformable, + cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null + }); + } + ); + } + + function updateRectShape(controller, cover, name, x, y, w, h) { + var el = cover.childOfName(name); + el && el.setShape(pointsToRect( + clipByPanel(controller, cover, [[x, y], [x + w, y + h]]) + )); + } + + function makeStyle(brushOption) { + return zrUtil.defaults({strokeNoScale: true}, brushOption.brushStyle); + } + + function formatRectRange(x, y, x2, y2) { + var min = [mathMin(x, x2), mathMin(y, y2)]; + var max = [mathMax(x, x2), mathMax(y, y2)]; + + return [ + [min[0], max[0]], // x range + [min[1], max[1]] // y range + ]; + } + + function getTransform(controller) { + return graphic.getTransform(controller.group); + } + + function getGlobalDirection(controller, localDirection) { + if (localDirection.length > 1) { + localDirection = localDirection.split(''); + var globalDir = [ + getGlobalDirection(controller, localDirection[0]), + getGlobalDirection(controller, localDirection[1]) + ]; + (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse(); + return globalDir.join(''); + } + else { + var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'}; + var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'}; + var globalDir = graphic.transformDirection( + map[localDirection], getTransform(controller) + ); + return inverseMap[globalDir]; + } + } + + function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) { + var brushOption = cover.__brushOption; + var rectRange = toRectRange(brushOption.range); + var localDelta = toLocalDelta(controller, dx, dy); + + each(name.split(''), function (namePart) { + var ind = DIRECTION_MAP[namePart]; + rectRange[ind[0]][ind[1]] += localDelta[ind[0]]; + }); + + brushOption.range = fromRectRange(formatRectRange( + rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1] + )); + + updateCoverAfterCreation(controller, cover); + trigger(controller, {isEnd: false}); + } + + function driftPolygon(controller, cover, dx, dy, e) { + var range = cover.__brushOption.range; + var localDelta = toLocalDelta(controller, dx, dy); + + each(range, function (point) { + point[0] += localDelta[0]; + point[1] += localDelta[1]; + }); + + updateCoverAfterCreation(controller, cover); + trigger(controller, {isEnd: false}); + } + + function toLocalDelta(controller, dx, dy) { + var thisGroup = controller.group; + var localD = thisGroup.transformCoordToLocal(dx, dy); + var localZero = thisGroup.transformCoordToLocal(0, 0); + + return [localD[0] - localZero[0], localD[1] - localZero[1]]; + } + + function clipByPanel(controller, cover, data) { + var panel = getPanelByCover(controller, cover); + if (panel === true) { // Global panel + return zrUtil.clone(data); + } + + var panelRect = panel.getBoundingRect(); + + return zrUtil.map(data, function (point) { + var x = point[0]; + x = mathMax(x, panelRect.x); + x = mathMin(x, panelRect.x + panelRect.width); + var y = point[1]; + y = mathMax(y, panelRect.y); + y = mathMin(y, panelRect.y + panelRect.height); + return [x, y]; + }); + } + + function pointsToRect(points) { + var xmin = mathMin(points[0][0], points[1][0]); + var ymin = mathMin(points[0][1], points[1][1]); + var xmax = mathMax(points[0][0], points[1][0]); + var ymax = mathMax(points[0][1], points[1][1]); + + return { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + } + + function resetCursor(controller, e) { + var x = e.offsetX; + var y = e.offsetY; + var zr = controller._zr; + + if (controller._brushType) { // If active + var panels = controller._panels; + var covers = controller._covers; + var inCover; + + for (var i = 0; i < covers.length; i++) { + if (coverRenderers[covers[i].__brushOption.brushType].contain(covers[i], x, y)) { + inCover = true; + break; + } + } + + if (!inCover) { + if (panels) { // Brush on panels + each(panels, function (panel) { + panel.contain(x, y) && zr.setCursorStyle('crosshair'); + }); + } + else { // Global brush + zr.setCursorStyle('crosshair'); + } + } + } + } + + function preventDefault(e) { + var rawE = e.event; + rawE.preventDefault && rawE.preventDefault(); + } + + function mainShapeContain(cover, x, y) { + return cover.childOfName('main').contain(x, y); + } + + function updateCoverByMouse(controller, e, isEnd) { + var x = e.offsetX; + var y = e.offsetY; + var creatingCover = controller._creatingCover; + var panel = controller._creatingPanel; + var thisBrushOption = controller._brushOption; + var eventParams; + + controller._track.push(controller.group.transformCoordToLocal(x, y)); + + if (shouldShowCover(controller) || creatingCover) { + + if (panel && !creatingCover) { + thisBrushOption.brushMode === 'single' && clearCovers(controller); + var brushOption = zrUtil.clone(thisBrushOption); + brushOption.panelId = panel === true ? null : panel.__brushPanelId; + creatingCover = controller._creatingCover = createCover(controller, brushOption); + controller._covers.push(creatingCover); + } + + if (creatingCover) { + var coverRenderer = coverRenderers[controller._brushType]; + var coverBrushOption = creatingCover.__brushOption; + + coverBrushOption.range = coverRenderer.getCreatingRange( + clipByPanel(controller, creatingCover, controller._track) + ); + + if (isEnd) { + endCreating(controller, creatingCover); + coverRenderer.updateCommon(controller, creatingCover); + } + + updateCoverShape(controller, creatingCover); + + eventParams = {isEnd: isEnd}; + } + } + else if ( + isEnd + && thisBrushOption.brushMode === 'single' + && thisBrushOption.removeOnClick + ) { + // Help user to remove covers easily, only by a tiny drag, in 'single' mode. + // But a single click do not clear covers, because user may have casual + // clicks (for example, click on other component and do not expect covers + // disappear). + // Only some cover removed, trigger action, but not every click trigger action. + if (getPanelByPoint(controller, x, y) && clearCovers(controller)) { + eventParams = {isEnd: isEnd, removeOnClick: true}; + } + } + + return eventParams; + } + + var mouseHandlers = { + + mousedown: function (e) { + if (this._dragging) { + // In case some browser do not support globalOut, + // and release mose out side the browser. + handleDragEnd.call(this, e); + } + else if (!e.target || !e.target.draggable) { + + preventDefault(e); + + var x = e.offsetX; + var y = e.offsetY; + + this._creatingCover = null; + var panel = this._creatingPanel = getPanelByPoint(this, x, y); + + if (panel) { + this._dragging = true; + this._track = [this.group.transformCoordToLocal(x, y)]; + } + } + }, + + mousemove: function (e) { + // set Cursor + resetCursor(this, e); + + if (this._dragging) { + + preventDefault(e); + + var eventParams = updateCoverByMouse(this, e, false); + + eventParams && trigger(this, eventParams); + } + }, + + mouseup: handleDragEnd //, + + // FIXME + // in tooltip, globalout should not be triggered. + // globalout: handleDragEnd + }; + + function handleDragEnd(e) { + if (this._dragging) { + + preventDefault(e); + + var eventParams = updateCoverByMouse(this, e, true); + + this._dragging = false; + this._track = []; + this._creatingCover = null; + + // trigger event shoule be at final, after procedure will be nested. + eventParams && trigger(this, eventParams); + } + } + + /** + * key: brushType + * @type {Object} + */ + var coverRenderers = { + + lineX: getLineRenderer(0), + + lineY: getLineRenderer(1), + + rect: { + createCover: function (controller, brushOption) { + return createBaseRectCover( + curry( + driftRect, + function (range) { + return range; + }, + function (range) { + return range; + } + ), + controller, + brushOption, + ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'] + ); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + updateBaseRect(controller, cover, localRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }, + + polygon: { + createCover: function (controller, brushOption) { + var cover = new graphic.Group(); + + // Do not use graphic.Polygon because graphic.Polyline do not close the + // border of the shape when drawing, which is a better experience for user. + cover.add(new graphic.Polyline({ + name: 'main', + style: makeStyle(brushOption), + silent: true + })); + + return cover; + }, + getCreatingRange: function (localTrack) { + return localTrack; + }, + endCreating: function (controller, cover) { + cover.remove(cover.childAt(0)); + // Use graphic.Polygon close the shape. + cover.add(new graphic.Polygon({ + name: 'main', + draggable: true, + drift: curry(driftPolygon, controller, cover), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + cover.childAt(0).setShape({ + points: clipByPanel(controller, cover, localRange) + }); + }, + updateCommon: updateCommon, + contain: mainShapeContain + } + }; + + function getLineRenderer(xyIndex) { + return { + createCover: function (controller, brushOption) { + return createBaseRectCover( + curry( + driftRect, + function (range) { + var rectRange = [range, [0, 100]]; + xyIndex && rectRange.reverse(); + return rectRange; + }, + function (rectRange) { + return rectRange[xyIndex]; + } + ), + controller, + brushOption, + [['w', 'e'], ['n', 's']][xyIndex] + ); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]); + var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]); + + return [min, max]; + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + var brushWidth = brushOption.brushStyle.width; + var otherExtent; + // If brushWidth not specified, fit the panel. + if (brushWidth == null) { + var panel = getPanelByCover(controller, cover); + var base = 0; + if (panel !== true) { + var rect = panel.getBoundingRect(); + brushWidth = xyIndex ? rect.width : rect.height; + base = xyIndex ? rect.x : rect.y; + } + // FIXME + // do not support global panel yet. + otherExtent = [base, base + (brushWidth || 0)]; + } + else { + otherExtent = [-brushWidth / 2, brushWidth / 2]; + } + var rectRange = [localRange, otherExtent]; + xyIndex && rectRange.reverse(); + + updateBaseRect(controller, cover, rectRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }; + } + + module.exports = BrushController; + + +/***/ }, +/* 234 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + module.exports = function (option) { + createParallelIfNeeded(option); + mergeAxisOptionFromParallel(option); + }; + + /** + * Create a parallel coordinate if not exists. + * @inner + */ + function createParallelIfNeeded(option) { + if (option.parallel) { + return; + } + + var hasParallelSeries = false; + + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'parallel') { + hasParallelSeries = true; + } + }); + + if (hasParallelSeries) { + option.parallel = [{}]; + } + } + + /** + * Merge aixs definition from parallel option (if exists) to axis option. + * @inner + */ + function mergeAxisOptionFromParallel(option) { + var axes = modelUtil.normalizeToArray(option.parallelAxis); + + zrUtil.each(axes, function (axisOption) { + if (!zrUtil.isObject(axisOption)) { + return; + } + + var parallelIndex = axisOption.parallelIndex || 0; + var parallelOption = modelUtil.normalizeToArray(option.parallel)[parallelIndex]; + + if (parallelOption && parallelOption.parallelAxisDefault) { + zrUtil.merge(axisOption, parallelOption.parallelAxisDefault, false); + } + }); + } + + + +/***/ }, +/* 235 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var completeDimensions = __webpack_require__(102); + + module.exports = SeriesModel.extend({ + + type: 'series.parallel', + + dependencies: ['parallel'], + + getInitialData: function (option, ecModel) { + var parallelModel = ecModel.getComponent( + 'parallel', this.get('parallelIndex') + ); + var parallelAxisIndices = parallelModel.parallelAxisIndex; + + var rawData = option.data; + var modelDims = parallelModel.dimensions; + + var dataDims = generateDataDims(modelDims, rawData); + + var dataDimsInfo = zrUtil.map(dataDims, function (dim, dimIndex) { + + var modelDimsIndex = zrUtil.indexOf(modelDims, dim); + var axisModel = modelDimsIndex >= 0 && ecModel.getComponent( + 'parallelAxis', parallelAxisIndices[modelDimsIndex] + ); + + if (axisModel && axisModel.get('type') === 'category') { + translateCategoryValue(axisModel, dim, rawData); + return {name: dim, type: 'ordinal'}; + } + else if (modelDimsIndex < 0) { + return completeDimensions.guessOrdinal(rawData, dimIndex) + ? {name: dim, type: 'ordinal'} + : dim; + } + else { + return dim; + } + }); + + var list = new List(dataDimsInfo, this); + list.initData(rawData); + + // Anication is forbiden in progressive data mode. + if (this.option.progressive) { + this.option.animation = false; + } + + return list; + }, + + /** + * User can get data raw indices on 'axisAreaSelected' event received. + * + * @public + * @param {string} activeState 'active' or 'inactive' or 'normal' + * @return {Array.} Raw indices + */ + getRawIndicesByActiveState: function (activeState) { + var coordSys = this.coordinateSystem; + var data = this.getData(); + var indices = []; + + coordSys.eachActiveState(data, function (theActiveState, dataIndex) { + if (activeState === theActiveState) { + indices.push(data.getRawIndex(dataIndex)); + } + }); + + return indices; + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + + coordinateSystem: 'parallel', + parallelIndex: 0, + + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + + inactiveOpacity: 0.05, + activeOpacity: 1, + + lineStyle: { + normal: { + width: 1, + opacity: 0.45, + type: 'solid' + } + }, + progressive: false, // 100 + smooth: false, + + animationEasing: 'linear' + } + }); + + function translateCategoryValue(axisModel, dim, rawData) { + var axisData = axisModel.get('data'); + var numberDim = convertDimNameToNumber(dim); + + if (axisData && axisData.length) { + zrUtil.each(rawData, function (dataItem) { + if (!dataItem) { + return; + } + // FIXME + // time consuming, should use hash? + var index = zrUtil.indexOf(axisData, dataItem[numberDim]); + dataItem[numberDim] = index >= 0 ? index : NaN; + }); + } + // FIXME + // 如果没有设置axis data, 应自动算出,或者提示。 + } + + function convertDimNameToNumber(dimName) { + return +dimName.replace('dim', ''); + } + + function generateDataDims(modelDims, rawData) { + // parallelModel.dimension should not be regarded as data + // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; + + // We detect max dim by parallelModel.dimensions and fist + // item in rawData arbitrarily. + var maxDimNum = 0; + zrUtil.each(modelDims, function (dimName) { + var numberDim = convertDimNameToNumber(dimName); + numberDim > maxDimNum && (maxDimNum = numberDim); + }); + + var firstItem = rawData[0]; + if (firstItem && firstItem.length - 1 > maxDimNum) { + maxDimNum = firstItem.length - 1; + } + + var dataDims = []; + for (var i = 0; i <= maxDimNum; i++) { + dataDims.push('dim' + i); + } + + return dataDims; + } + + +/***/ }, +/* 236 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + var SMOOTH = 0.3; + + var ParallelView = __webpack_require__(42).extend({ + + type: 'parallel', + + init: function () { + + /** + * @type {module:zrender/container/Group} + * @private + */ + this._dataGroup = new graphic.Group(); + + this.group.add(this._dataGroup); + + /** + * @type {module:echarts/data/List} + */ + this._data; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + this._renderForNormal(seriesModel); + // this[ + // seriesModel.option.progressive + // ? '_renderForProgressive' + // : '_renderForNormal' + // ](seriesModel); + }, + + /** + * @private + */ + _renderForNormal: function (seriesModel) { + var dataGroup = this._dataGroup; + var data = seriesModel.getData(); + var oldData = this._data; + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var option = seriesModel.option; + var smooth = option.smooth ? SMOOTH : null; + + // Consider switch between progressive and not. + // oldData && oldData.__plProgressive && dataGroup.removeAll(); + + data.diff(oldData) + .add(add) + .update(update) + .remove(remove) + .execute(); + + // Update style + updateElCommon(data, smooth); + + // First create + if (!this._data) { + var clipPath = createGridClipShape( + coordSys, seriesModel, function () { + // Callback will be invoked immediately if there is no animation + setTimeout(function () { + dataGroup.removeClipPath(); + }); + } + ); + dataGroup.setClipPath(clipPath); + } + + this._data = data; + + function add(newDataIndex) { + addEl(data, dataGroup, newDataIndex, dimensions, coordSys, null, smooth); + } + + function update(newDataIndex, oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + var points = createLinePoints(data, newDataIndex, dimensions, coordSys); + data.setItemGraphicEl(newDataIndex, line); + graphic.updateProps(line, {shape: {points: points}}, seriesModel, newDataIndex); + } + + function remove(oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + dataGroup.remove(line); + } + + }, + + /** + * @private + */ + // _renderForProgressive: function (seriesModel) { + // var dataGroup = this._dataGroup; + // var data = seriesModel.getData(); + // var oldData = this._data; + // var coordSys = seriesModel.coordinateSystem; + // var dimensions = coordSys.dimensions; + // var option = seriesModel.option; + // var progressive = option.progressive; + // var smooth = option.smooth ? SMOOTH : null; + + // // In progressive animation is disabled, so use simple data diff, + // // which effects performance less. + // // (Typically performance for data with length 7000+ like: + // // simpleDiff: 60ms, addEl: 184ms, + // // in RMBP 2.4GHz intel i7, OSX 10.9 chrome 50.0.2661.102 (64-bit)) + // if (simpleDiff(oldData, data, dimensions)) { + // dataGroup.removeAll(); + // data.each(function (dataIndex) { + // addEl(data, dataGroup, dataIndex, dimensions, coordSys); + // }); + // } + + // updateElCommon(data, progressive, smooth); + + // // Consider switch between progressive and not. + // data.__plProgressive = true; + // this._data = data; + // }, + + /** + * @override + */ + remove: function () { + this._dataGroup && this._dataGroup.removeAll(); + this._data = null; + } + }); + + function createGridClipShape(coordSys, seriesModel, cb) { + var parallelModel = coordSys.model; + var rect = coordSys.getRect(); + var rectEl = new graphic.Rect({ + shape: { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + } + }); + + var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height'; + rectEl.setShape(dim, 0); + graphic.initProps(rectEl, { + shape: { + width: rect.width, + height: rect.height + } + }, seriesModel, cb); + return rectEl; + } + + function createLinePoints(data, dataIndex, dimensions, coordSys) { + var points = []; + for (var i = 0; i < dimensions.length; i++) { + var dimName = dimensions[i]; + var value = data.get(dimName, dataIndex); + if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) { + points.push(coordSys.dataToPoint(value, dimName)); + } + } + return points; + } + + function addEl(data, dataGroup, dataIndex, dimensions, coordSys) { + var points = createLinePoints(data, dataIndex, dimensions, coordSys); + var line = new graphic.Polyline({ + shape: {points: points}, + silent: true, + z2: 10 + }); + dataGroup.add(line); + data.setItemGraphicEl(dataIndex, line); + } + + function updateElCommon(data, smooth) { + var seriesStyleModel = data.hostModel.getModel('lineStyle.normal'); + var lineStyle = seriesStyleModel.getLineStyle(); + data.eachItemGraphicEl(function (line, dataIndex) { + if (data.hasItemOption) { + var itemModel = data.getItemModel(dataIndex); + var lineStyleModel = itemModel.getModel('lineStyle.normal', seriesStyleModel); + lineStyle = lineStyleModel.getLineStyle(); + } + + line.useStyle(zrUtil.extend( + lineStyle, + { + fill: null, + stroke: data.getItemVisual(dataIndex, 'color'), + opacity: data.getItemVisual(dataIndex, 'opacity') + } + )); + line.shape.smooth = smooth; + }); + } + + // function simpleDiff(oldData, newData, dimensions) { + // var oldLen; + // if (!oldData + // || !oldData.__plProgressive + // || (oldLen = oldData.count()) !== newData.count() + // ) { + // return true; + // } + + // var dimLen = dimensions.length; + // for (var i = 0; i < oldLen; i++) { + // for (var j = 0; j < dimLen; j++) { + // if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) { + // return true; + // } + // } + // } + + // return false; + // } + + // FIXME + // 公用方法? + function isEmptyValue(val, axisType) { + return axisType === 'category' + ? val == null + : (val == null || isNaN(val)); // axisType === 'value' + } + + module.exports = ParallelView; + + +/***/ }, +/* 237 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + + ecModel.eachSeriesByType('parallel', function (seriesModel) { + + var itemStyleModel = seriesModel.getModel('itemStyle.normal'); + var lineStyleModel = seriesModel.getModel('lineStyle.normal'); + var globalColors = ecModel.get('color'); + + var color = lineStyleModel.get('color') + || itemStyleModel.get('color') + || globalColors[seriesModel.seriesIndex % globalColors.length]; + var inactiveOpacity = seriesModel.get('inactiveOpacity'); + var activeOpacity = seriesModel.get('activeOpacity'); + var lineStyle = seriesModel.getModel('lineStyle.normal').getLineStyle(); + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + + var opacityMap = { + normal: lineStyle.opacity, + active: activeOpacity, + inactive: inactiveOpacity + }; + + coordSys.eachActiveState(data, function (activeState, dataIndex) { + data.setItemVisual(dataIndex, 'opacity', opacityMap[activeState]); + }); + + data.setVisual('color', color); + }); + }; + + +/***/ }, +/* 238 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(239); + __webpack_require__(240); + echarts.registerLayout(__webpack_require__(241)); + echarts.registerVisual(__webpack_require__(243)); + + +/***/ }, +/* 239 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Get initial data and define sankey view's series model + * @author Deqing Li(annong035@gmail.com) + */ + + + var SeriesModel = __webpack_require__(28); + var createGraphFromNodeEdge = __webpack_require__(196); + + var SankeySeries = SeriesModel.extend({ + + type: 'series.sankey', + + layoutInfo: null, + + /** + * Init a graph data structure from data in option series + * + * @param {Object} option the object used to config echarts view + * @return {module:echarts/data/List} storage initial data + */ + getInitialData: function (option) { + var links = option.edges || option.links; + var nodes = option.data || option.nodes; + if (nodes && links) { + var graph = createGraphFromNodeEdge(nodes, links, this, true); + return graph.data; + } + }, + + /** + * Return the graphic data structure + * + * @return {module:echarts/data/Graph} graphic data structure + */ + getGraph: function () { + return this.getData().graph; + }, + + /** + * Get edge data of graphic data structure + * + * @return {module:echarts/data/List} data structure of list + */ + getEdgeData: function () { + return this.getGraph().edgeData; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + // dataType === 'node' or empty do not show tooltip by default + if (dataType === 'edge') { + var params = this.getDataParams(dataIndex, dataType); + var rawDataOpt = params.data; + var html = rawDataOpt.source + ' -- ' + rawDataOpt.target; + if (params.value) { + html += ' : ' + params.value; + } + return html; + } + + return SankeySeries.superCall(this, 'formatTooltip', dataIndex, multipleSeries); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + coordinateSystem: 'view', + + layout: null, + + // the position of the whole view + left: '5%', + top: '5%', + right: '20%', + bottom: '5%', + + // the dx of the node + nodeWidth: 20, + + // the vertical distance between two nodes + nodeGap: 8, + + // the number of iterations to change the position of the node + layoutIterations: 32, + + label: { + normal: { + show: true, + position: 'right', + textStyle: { + color: '#000', + fontSize: 12 + } + }, + emphasis: { + show: true + } + }, + + itemStyle: { + normal: { + borderWidth: 1, + borderColor: '#333' + } + }, + + lineStyle: { + normal: { + color: '#314656', + opacity: 0.2, + curveness: 0.5 + }, + emphasis: { + opacity: 0.6 + } + }, + + animationEasing: 'linear', + + animationDuration: 1000 + } + + }); + + module.exports = SankeySeries; + + + +/***/ }, +/* 240 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file The file used to draw sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + var SankeyShape = graphic.extendShape({ + shape: { + x1: 0, y1: 0, + x2: 0, y2: 0, + cpx1: 0, cpy1: 0, + cpx2: 0, cpy2: 0, + + extent: 0 + }, + + buildPath: function (ctx, shape) { + var halfExtent = shape.extent / 2; + ctx.moveTo(shape.x1, shape.y1 - halfExtent); + ctx.bezierCurveTo( + shape.cpx1, shape.cpy1 - halfExtent, + shape.cpx2, shape.cpy2 - halfExtent, + shape.x2, shape.y2 - halfExtent + ); + ctx.lineTo(shape.x2, shape.y2 + halfExtent); + ctx.bezierCurveTo( + shape.cpx2, shape.cpy2 + halfExtent, + shape.cpx1, shape.cpy1 + halfExtent, + shape.x1, shape.y1 + halfExtent + ); + ctx.closePath(); + } + }); + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'sankey', + + /** + * @private + * @type {module:echarts/chart/sankey/SankeySeries} + */ + _model: null, + + render: function (seriesModel, ecModel, api) { + var graph = seriesModel.getGraph(); + var group = this.group; + var layoutInfo = seriesModel.layoutInfo; + var nodeData = seriesModel.getData(); + var edgeData = seriesModel.getData('edge'); + + this._model = seriesModel; + + group.removeAll(); + + group.position = [layoutInfo.x, layoutInfo.y]; + + // generate a bezire Curve for each edge + graph.eachEdge(function (edge) { + var curve = new SankeyShape(); + + curve.dataIndex = edge.dataIndex; + curve.seriesIndex = seriesModel.seriesIndex; + curve.dataType = 'edge'; + + var lineStyleModel = edge.getModel('lineStyle.normal'); + var curvature = lineStyleModel.get('curveness'); + var n1Layout = edge.node1.getLayout(); + var n2Layout = edge.node2.getLayout(); + var edgeLayout = edge.getLayout(); + + curve.shape.extent = Math.max(1, edgeLayout.dy); + + var x1 = n1Layout.x + n1Layout.dx; + var y1 = n1Layout.y + edgeLayout.sy + edgeLayout.dy / 2; + var x2 = n2Layout.x; + var y2 = n2Layout.y + edgeLayout.ty + edgeLayout.dy / 2; + var cpx1 = x1 * (1 - curvature) + x2 * curvature; + var cpy1 = y1; + var cpx2 = x1 * curvature + x2 * (1 - curvature); + var cpy2 = y2; + + curve.setShape({ + x1: x1, + y1: y1, + x2: x2, + y2: y2, + cpx1: cpx1, + cpy1: cpy1, + cpx2: cpx2, + cpy2: cpy2 + }); + + curve.setStyle(lineStyleModel.getItemStyle()); + // Special color, use source node color or target node color + switch (curve.style.fill) { + case 'source': + curve.style.fill = edge.node1.getVisual('color'); + break; + case 'target': + curve.style.fill = edge.node2.getVisual('color'); + break; + } + + graphic.setHoverStyle(curve, edge.getModel('lineStyle.emphasis').getItemStyle()); + + group.add(curve); + + edgeData.setItemGraphicEl(edge.dataIndex, curve); + }); + + // generate a rect for each node + graph.eachNode(function (node) { + var layout = node.getLayout(); + var itemModel = node.getModel(); + var labelModel = itemModel.getModel('label.normal'); + var textStyleModel = labelModel.getModel('textStyle'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var textStyleHoverModel = labelHoverModel.getModel('textStyle'); + + var rect = new graphic.Rect({ + shape: { + x: layout.x, + y: layout.y, + width: node.getLayout().dx, + height: node.getLayout().dy + }, + style: { + // Get formatted label in label.normal option + // Use node id if it is not specified + text: labelModel.get('show') + ? seriesModel.getFormattedLabel(node.dataIndex, 'normal') || node.id + // Use empty string to hide the label + : '', + textFont: textStyleModel.getFont(), + textFill: textStyleModel.getTextColor(), + textPosition: labelModel.get('position') + } + }); + + rect.setStyle(zrUtil.defaults( + { + fill: node.getVisual('color') + }, + itemModel.getModel('itemStyle.normal').getItemStyle() + )); + + graphic.setHoverStyle(rect, zrUtil.extend( + node.getModel('itemStyle.emphasis'), + { + text: labelHoverModel.get('show') + ? seriesModel.getFormattedLabel(node.dataIndex, 'emphasis') || node.id + : '', + textFont: textStyleHoverModel.getFont(), + textFill: textStyleHoverModel.getTextColor(), + textPosition: labelHoverModel.get('position') + } + )); + + group.add(rect); + + nodeData.setItemGraphicEl(node.dataIndex, rect); + + rect.dataType = 'node'; + }); + + if (!this._data && seriesModel.get('animation')) { + group.setClipPath(createGridClipShape(group.getBoundingRect(), seriesModel, function () { + group.removeClipPath(); + })); + } + + this._data = seriesModel.getData(); + } + }); + + // add animation to the view + function createGridClipShape(rect, seriesModel, cb) { + var rectEl = new graphic.Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + graphic.initProps(rectEl, { + shape: { + width: rect.width + 20, + height: rect.height + 20 + } + }, seriesModel, cb); + + return rectEl; + } + + + +/***/ }, +/* 241 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file The layout algorithm of sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var layout = __webpack_require__(21); + var nest = __webpack_require__(242); + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel, api, payload) { + + ecModel.eachSeriesByType('sankey', function (seriesModel) { + + var nodeWidth = seriesModel.get('nodeWidth'); + var nodeGap = seriesModel.get('nodeGap'); + + var layoutInfo = getViewRect(seriesModel, api); + + seriesModel.layoutInfo = layoutInfo; + + var width = layoutInfo.width; + var height = layoutInfo.height; + + var graph = seriesModel.getGraph(); + + var nodes = graph.nodes; + var edges = graph.edges; + + computeNodeValues(nodes); + + var filteredNodes = nodes.filter(function (node) { + return node.getLayout().value === 0; + }); + + var iterations = filteredNodes.length !== 0 + ? 0 : seriesModel.get('layoutIterations'); + + layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations); + }); + }; + + /** + * Get the layout position of the whole view + * + * @param {module:echarts/model/Series} seriesModel the model object of sankey series + * @param {module:echarts/ExtensionAPI} api provide the API list that the developer can call + * @return {module:zrender/core/BoundingRect} size of rect to draw the sankey view + */ + function getViewRect(seriesModel, api) { + return layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); + } + + function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations) { + computeNodeBreadths(nodes, nodeWidth, width); + computeNodeDepths(nodes, edges, height, nodeGap, iterations); + computeEdgeDepths(nodes); + } + + /** + * Compute the value of each node by summing the associated edge's value + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + */ + function computeNodeValues(nodes) { + zrUtil.each(nodes, function (node) { + var value1 = sum(node.outEdges, getEdgeValue); + var value2 = sum(node.inEdges, getEdgeValue); + var value = Math.max(value1, value2); + node.setLayout({value: value}, true); + }); + } + + /** + * Compute the x-position for each node + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} nodeWidth the dx of the node + * @param {number} width the whole width of the area to draw the view + */ + function computeNodeBreadths(nodes, nodeWidth, width) { + var remainNodes = nodes; + var nextNode = null; + var x = 0; + var kx = 0; + + while (remainNodes.length) { + nextNode = []; + for (var i = 0, len = remainNodes.length; i < len; i++) { + var node = remainNodes[i]; + node.setLayout({x: x}, true); + node.setLayout({dx: nodeWidth}, true); + for (var j = 0, lenj = node.outEdges.length; j < lenj; j++) { + nextNode.push(node.outEdges[j].node2); + } + } + remainNodes = nextNode; + ++x; + } + + moveSinksRight(nodes, x); + kx = (width - nodeWidth) / (x - 1); + + scaleNodeBreadths(nodes, kx); + } + + /** + * All the node without outEgdes are assigned maximum x-position and + * be aligned in the last column. + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} x value (x-1) use to assign to node without outEdges + * as x-position + */ + function moveSinksRight(nodes, x) { + zrUtil.each(nodes, function (node) { + if (!node.outEdges.length) { + node.setLayout({x: x - 1}, true); + } + }); + } + + /** + * Scale node x-position to the width + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} kx multiple used to scale nodes + */ + function scaleNodeBreadths(nodes, kx) { + zrUtil.each(nodes, function (node) { + var nodeX = node.getLayout().x * kx; + node.setLayout({x: nodeX}, true); + }); + } + + /** + * Using Gauss-Seidel iterations method to compute the node depth(y-position) + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {module:echarts/data/Graph~Edge} edges edge of sankey view + * @param {number} height the whole height of the area to draw the view + * @param {numbber} nodeGap the vertical distance between two nodes + * in the same column. + * @param {number} iterations the number of iterations for the algorithm + */ + function computeNodeDepths(nodes, edges, height, nodeGap, iterations) { + var nodesByBreadth = nest() + .key(function (d) { + return d.getLayout().x; + }) + .sortKeys(ascending) + .entries(nodes) + .map(function (d) { + return d.values; + }); + + initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap); + resolveCollisions(nodesByBreadth, nodeGap, height); + + for (var alpha = 1; iterations > 0; iterations--) { + // 0.99 is a experience parameter, ensure that each iterations of + // changes as small as possible. + alpha *= 0.99; + relaxRightToLeft(nodesByBreadth, alpha); + resolveCollisions(nodesByBreadth, nodeGap, height); + relaxLeftToRight(nodesByBreadth, alpha); + resolveCollisions(nodesByBreadth, nodeGap, height); + } + } + + /** + * Compute the original y-position for each node + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the nodes x-position. + * @param {module:echarts/data/Graph~Edge} edges edge of sankey view + * @param {number} height the whole height of the area to draw the view + * @param {number} nodeGap the vertical distance between two nodes + */ + function initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap) { + var kyArray = []; + zrUtil.each(nodesByBreadth, function (nodes) { + var n = nodes.length; + var sum = 0; + zrUtil.each(nodes, function (node) { + sum += node.getLayout().value; + }); + var ky = (height - (n - 1) * nodeGap) / sum; + kyArray.push(ky); + }); + + kyArray.sort(function (a, b) { + return a - b; + }); + var ky0 = kyArray[0]; + + zrUtil.each(nodesByBreadth, function (nodes) { + zrUtil.each(nodes, function (node, i) { + node.setLayout({y: i}, true); + var nodeDy = node.getLayout().value * ky0; + node.setLayout({dy: nodeDy}, true); + }); + }); + + zrUtil.each(edges, function (edge) { + var edgeDy = +edge.getValue() * ky0; + edge.setLayout({dy: edgeDy}, true); + }); + } + + /** + * Resolve the collision of initialized depth (y-position) + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the nodes x-position. + * @param {number} nodeGap the vertical distance between two nodes + * @param {number} height the whole height of the area to draw the view + */ + function resolveCollisions(nodesByBreadth, nodeGap, height) { + zrUtil.each(nodesByBreadth, function (nodes) { + var node; + var dy; + var y0 = 0; + var n = nodes.length; + var i; + + nodes.sort(ascendingDepth); + + for (i = 0; i < n; i++) { + node = nodes[i]; + dy = y0 - node.getLayout().y; + if (dy > 0) { + var nodeY = node.getLayout().y + dy; + node.setLayout({y: nodeY}, true); + } + y0 = node.getLayout().y + node.getLayout().dy + nodeGap; + } + + // if the bottommost node goes outside the bounds, push it back up + dy = y0 - nodeGap - height; + if (dy > 0) { + var nodeY = node.getLayout().y - dy; + node.setLayout({y: nodeY}, true); + y0 = node.getLayout().y; + for (i = n - 2; i >= 0; --i) { + node = nodes[i]; + dy = node.getLayout().y + node.getLayout().dy + nodeGap - y0; + if (dy > 0) { + nodeY = node.getLayout().y - dy; + node.setLayout({y: nodeY}, true); + } + y0 = node.getLayout().y; + } + } + }); + } + + /** + * Change the y-position of the nodes, except most the right side nodes + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the node x-position. + * @param {number} alpha parameter used to adjust the nodes y-position + */ + function relaxRightToLeft(nodesByBreadth, alpha) { + zrUtil.each(nodesByBreadth.slice().reverse(), function (nodes) { + zrUtil.each(nodes, function (node) { + if (node.outEdges.length) { + var y = sum(node.outEdges, weightedTarget) / sum(node.outEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); + } + + function weightedTarget(edge) { + return center(edge.node2) * edge.getValue(); + } + + /** + * Change the y-position of the nodes, except most the left side nodes + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the node x-position. + * @param {number} alpha parameter used to adjust the nodes y-position + */ + function relaxLeftToRight(nodesByBreadth, alpha) { + zrUtil.each(nodesByBreadth, function (nodes) { + zrUtil.each(nodes, function (node) { + if (node.inEdges.length) { + var y = sum(node.inEdges, weightedSource) / sum(node.inEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); + } + + function weightedSource(edge) { + return center(edge.node1) * edge.getValue(); + } + + /** + * Compute the depth(y-position) of each edge + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + */ + function computeEdgeDepths(nodes) { + zrUtil.each(nodes, function (node) { + node.outEdges.sort(ascendingTargetDepth); + node.inEdges.sort(ascendingSourceDepth); + }); + zrUtil.each(nodes, function (node) { + var sy = 0; + var ty = 0; + zrUtil.each(node.outEdges, function (edge) { + edge.setLayout({sy: sy}, true); + sy += edge.getLayout().dy; + }); + zrUtil.each(node.inEdges, function (edge) { + edge.setLayout({ty: ty}, true); + ty += edge.getLayout().dy; + }); + }); + } + + function ascendingTargetDepth(a, b) { + return a.node2.getLayout().y - b.node2.getLayout().y; + } + + function ascendingSourceDepth(a, b) { + return a.node1.getLayout().y - b.node1.getLayout().y; + } + + function sum(array, f) { + var sum = 0; + var len = array.length; + var i = -1; + while (++i < len) { + var value = +f.call(array, array[i], i); + if (!isNaN(value)) { + sum += value; + } + } + return sum; + } + + function center(node) { + return node.getLayout().y + node.getLayout().dy / 2; + } + + function ascendingDepth(a, b) { + return a.getLayout().y - b.getLayout().y; + } + + function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a === b ? 0 : NaN; + } + + function getEdgeValue(edge) { + return edge.getValue(); + } + + + +/***/ }, +/* 242 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + /** + * nest helper used to group by the array. + * can specified the keys and sort the keys. + */ + function nest() { + + var keysFunction = []; + var sortKeysFunction = []; + + /** + * map an Array into the mapObject. + * @param {Array} array + * @param {number} depth + */ + function map(array, depth) { + if (depth >= keysFunction.length) { + return array; + } + var i = -1; + var n = array.length; + var keyFunction = keysFunction[depth++]; + var mapObject = {}; + var valuesByKey = {}; + + while (++i < n) { + var keyValue = keyFunction(array[i]); + var values = valuesByKey[keyValue]; + + if (values) { + values.push(array[i]); + } + else { + valuesByKey[keyValue] = [array[i]]; + } + } + + zrUtil.each(valuesByKey, function (value, key) { + mapObject[key] = map(value, depth); + }); + + return mapObject; + } + + /** + * transform the Map Object to multidimensional Array + * @param {Object} map + * @param {number} depth + */ + function entriesMap(mapObject, depth) { + if (depth >= keysFunction.length) { + return mapObject; + } + var array = []; + var sortKeyFunction = sortKeysFunction[depth++]; + + zrUtil.each(mapObject, function (value, key) { + array.push({ + key: key, values: entriesMap(value, depth) + }); + }); + + if (sortKeyFunction) { + return array.sort(function (a, b) { + return sortKeyFunction(a.key, b.key); + }); + } + else { + return array; + } + } + + return { + /** + * specified the key to groupby the arrays. + * users can specified one more keys. + * @param {Function} d + */ + key: function (d) { + keysFunction.push(d); + return this; + }, + + /** + * specified the comparator to sort the keys + * @param {Function} order + */ + sortKeys: function (order) { + sortKeysFunction[keysFunction.length - 1] = order; + return this; + }, + + /** + * the array to be grouped by. + * @param {Array} array + */ + entries: function (array) { + return entriesMap(map(array, 0), 0); + } + }; + } + module.exports = nest; + + +/***/ }, +/* 243 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual encoding for sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var VisualMapping = __webpack_require__(192); + + module.exports = function (ecModel, payload) { + ecModel.eachSeriesByType('sankey', function (seriesModel) { + var graph = seriesModel.getGraph(); + var nodes = graph.nodes; + + nodes.sort(function (a, b) { + return a.getLayout().value - b.getLayout().value; + }); + + var minValue = nodes[0].getLayout().value; + var maxValue = nodes[nodes.length - 1].getLayout().value; + + nodes.forEach(function (node) { + var mapping = new VisualMapping({ + type: 'color', + mappingMethod: 'linear', + dataExtent: [minValue, maxValue], + visual: seriesModel.get('color') + }); + + var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value); + node.setVisual('color', mapValueToColor); + // If set itemStyle.normal.color + var itemModel = node.getModel(); + var customColor = itemModel.get('itemStyle.normal.color'); + if (customColor != null) { + node.setVisual('color', customColor); + } + }); + + }); + }; + + + +/***/ }, +/* 244 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(245); + __webpack_require__(248); + + echarts.registerVisual(__webpack_require__(249)); + echarts.registerLayout(__webpack_require__(250)); + + + +/***/ }, +/* 245 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var whiskerBoxCommon = __webpack_require__(246); + + var BoxplotSeries = SeriesModel.extend({ + + type: 'series.boxplot', + + dependencies: ['xAxis', 'yAxis', 'grid'], + + // TODO + // box width represents group size, so dimension should have 'size'. + + /** + * @see + * The meanings of 'min' and 'max' depend on user, + * and echarts do not need to know it. + * @readOnly + */ + valueDimensions: ['min', 'Q1', 'median', 'Q3', 'max'], + + /** + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * @override + */ + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + + // xAxisIndex: 0, + // yAxisIndex: 0, + + layout: null, // 'horizontal' or 'vertical' + boxWidth: [7, 50], // [min, max] can be percent of band width. + + itemStyle: { + normal: { + color: '#fff', + borderWidth: 1 + }, + emphasis: { + borderWidth: 2, + shadowBlur: 5, + shadowOffsetX: 2, + shadowOffsetY: 2, + shadowColor: 'rgba(0,0,0,0.4)' + } + }, + + animationEasing: 'elasticOut', + animationDuration: 800 + } + }); + + zrUtil.mixin(BoxplotSeries, whiskerBoxCommon.seriesModelMixin, true); + + module.exports = BoxplotSeries; + + + +/***/ }, +/* 246 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var WhiskerBoxDraw = __webpack_require__(247); + var zrUtil = __webpack_require__(4); + + function getItemValue(item) { + return item.value == null ? item : item.value; + } + + var seriesModelMixin = { + + /** + * @private + * @type {string} + */ + _baseAxisDim: null, + + /** + * @override + */ + getInitialData: function (option, ecModel) { + // When both types of xAxis and yAxis are 'value', layout is + // needed to be specified by user. Otherwise, layout can be + // judged by which axis is category. + + var categories; + + var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex')); + var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex')); + var xAxisType = xAxisModel.get('type'); + var yAxisType = yAxisModel.get('type'); + var addOrdinal; + + // FIXME + // 考虑时间轴 + + if (xAxisType === 'category') { + option.layout = 'horizontal'; + categories = xAxisModel.getCategories(); + addOrdinal = true; + } + else if (yAxisType === 'category') { + option.layout = 'vertical'; + categories = yAxisModel.getCategories(); + addOrdinal = true; + } + else { + option.layout = option.layout || 'horizontal'; + } + + this._baseAxisDim = option.layout === 'horizontal' ? 'x' : 'y'; + + var data = option.data; + var dimensions = this.dimensions = ['base'].concat(this.valueDimensions); + completeDimensions(dimensions, data); + + var list = new List(dimensions, this); + list.initData(data, categories ? categories.slice() : null, function (dataItem, dimName, idx, dimIdx) { + var value = getItemValue(dataItem); + return addOrdinal ? (dimName === 'base' ? idx : value[dimIdx - 1]) : value[dimIdx]; + }); + + return list; + }, + + /** + * Used by Gird. + * @param {string} axisDim 'x' or 'y' + * @return {Array.} dimensions on the axis. + */ + coordDimToDataDim: function (axisDim) { + var dims = this.valueDimensions.slice(); + var baseDim = ['base']; + var map = { + horizontal: {x: baseDim, y: dims}, + vertical: {x: dims, y: baseDim} + }; + return map[this.get('layout')][axisDim]; + }, + + /** + * @override + * @param {string|number} dataDim + * @return {string} coord dimension + */ + dataDimToCoordDim: function (dataDim) { + var dim; + + zrUtil.each(['x', 'y'], function (coordDim, index) { + var dataDims = this.coordDimToDataDim(coordDim); + if (zrUtil.indexOf(dataDims, dataDim) >= 0) { + dim = coordDim; + } + }, this); + + return dim; + }, + + /** + * If horizontal, base axis is x, otherwise y. + * @override + */ + getBaseAxis: function () { + var dim = this._baseAxisDim; + return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis; + } + }; + + var viewMixin = { + + init: function () { + /** + * Old data. + * @private + * @type {module:echarts/chart/helper/WhiskerBoxDraw} + */ + var whiskerBoxDraw = this._whiskerBoxDraw = new WhiskerBoxDraw( + this.getStyleUpdater() + ); + this.group.add(whiskerBoxDraw.group); + }, + + render: function (seriesModel, ecModel, api) { + this._whiskerBoxDraw.updateData(seriesModel.getData()); + }, + + remove: function (ecModel) { + this._whiskerBoxDraw.remove(); + } + }; + + module.exports = { + seriesModelMixin: seriesModelMixin, + viewMixin: viewMixin + }; + + +/***/ }, +/* 247 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Symbol + */ + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Path = __webpack_require__(45); + + var WhiskerPath = Path.extend({ + + type: 'whiskerInBox', + + shape: {}, + + buildPath: function (ctx, shape) { + for (var i in shape) { + if (i.indexOf('ends') === 0) { + var pts = shape[i]; + ctx.moveTo(pts[0][0], pts[0][1]); + ctx.lineTo(pts[1][0], pts[1][1]); + } + } + } + }); + + /** + * @constructor + * @alias {module:echarts/chart/helper/WhiskerBox} + * @param {module:echarts/data/List} data + * @param {number} idx + * @param {Function} styleUpdater + * @param {boolean} isInit + * @extends {module:zrender/graphic/Group} + */ + function WhiskerBox(data, idx, styleUpdater, isInit) { + graphic.Group.call(this); + + /** + * @type {number} + * @readOnly + */ + this.bodyIndex; + + /** + * @type {number} + * @readOnly + */ + this.whiskerIndex; + + /** + * @type {Function} + */ + this.styleUpdater = styleUpdater; + + this._createContent(data, idx, isInit); + + this.updateData(data, idx, isInit); + + /** + * Last series model. + * @type {module:echarts/model/Series} + */ + this._seriesModel; + } + + var whiskerBoxProto = WhiskerBox.prototype; + + whiskerBoxProto._createContent = function (data, idx, isInit) { + var itemLayout = data.getItemLayout(idx); + var constDim = itemLayout.chartLayout === 'horizontal' ? 1 : 0; + var count = 0; + + // Whisker element. + this.add(new graphic.Polygon({ + shape: { + points: isInit + ? transInit(itemLayout.bodyEnds, constDim, itemLayout) + : itemLayout.bodyEnds + }, + style: {strokeNoScale: true}, + z2: 100 + })); + this.bodyIndex = count++; + + // Box element. + var whiskerEnds = zrUtil.map(itemLayout.whiskerEnds, function (ends) { + return isInit ? transInit(ends, constDim, itemLayout) : ends; + }); + this.add(new WhiskerPath({ + shape: makeWhiskerEndsShape(whiskerEnds), + style: {strokeNoScale: true}, + z2: 100 + })); + this.whiskerIndex = count++; + }; + + function transInit(points, dim, itemLayout) { + return zrUtil.map(points, function (point) { + point = point.slice(); + point[dim] = itemLayout.initBaseline; + return point; + }); + } + + function makeWhiskerEndsShape(whiskerEnds) { + // zr animation only support 2-dim array. + var shape = {}; + zrUtil.each(whiskerEnds, function (ends, i) { + shape['ends' + i] = ends; + }); + return shape; + } + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + whiskerBoxProto.updateData = function (data, idx, isInit) { + var seriesModel = this._seriesModel = data.hostModel; + var itemLayout = data.getItemLayout(idx); + var updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; + // this.childAt(this.bodyIndex).stopAnimation(true); + // this.childAt(this.whiskerIndex).stopAnimation(true); + updateMethod( + this.childAt(this.bodyIndex), + {shape: {points: itemLayout.bodyEnds}}, + seriesModel, idx + ); + updateMethod( + this.childAt(this.whiskerIndex), + {shape: makeWhiskerEndsShape(itemLayout.whiskerEnds)}, + seriesModel, idx + ); + + this.styleUpdater.call(null, this, data, idx); + }; + + zrUtil.inherits(WhiskerBox, graphic.Group); + + + /** + * @constructor + * @alias module:echarts/chart/helper/WhiskerBoxDraw + */ + function WhiskerBoxDraw(styleUpdater) { + this.group = new graphic.Group(); + this.styleUpdater = styleUpdater; + } + + var whiskerBoxDrawProto = WhiskerBoxDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + whiskerBoxDrawProto.updateData = function (data) { + var group = this.group; + var oldData = this._data; + var styleUpdater = this.styleUpdater; + + data.diff(oldData) + .add(function (newIdx) { + if (data.hasValue(newIdx)) { + var symbolEl = new WhiskerBox(data, newIdx, styleUpdater, true); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }) + .update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + + // Empty data + if (!data.hasValue(newIdx)) { + group.remove(symbolEl); + return; + } + + if (!symbolEl) { + symbolEl = new WhiskerBox(data, newIdx, styleUpdater); + } + else { + symbolEl.updateData(data, newIdx); + } + + // Add back + group.add(symbolEl); + + data.setItemGraphicEl(newIdx, symbolEl); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }) + .execute(); + + this._data = data; + }; + + /** + * Remove symbols. + * @param {module:echarts/data/List} data + */ + whiskerBoxDrawProto.remove = function () { + var group = this.group; + var data = this._data; + this._data = null; + data && data.eachItemGraphicEl(function (el) { + el && group.remove(el); + }); + }; + + module.exports = WhiskerBoxDraw; + + +/***/ }, +/* 248 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + var whiskerBoxCommon = __webpack_require__(246); + + var BoxplotView = ChartView.extend({ + + type: 'boxplot', + + getStyleUpdater: function () { + return updateStyle; + } + }); + + zrUtil.mixin(BoxplotView, whiskerBoxCommon.viewMixin, true); + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + + function updateStyle(itemGroup, data, idx) { + var itemModel = data.getItemModel(idx); + var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath); + var borderColor = data.getItemVisual(idx, 'color'); + + // Exclude borderColor. + var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']); + + var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex); + whiskerEl.style.set(itemStyle); + whiskerEl.style.stroke = borderColor; + whiskerEl.dirty(); + + var bodyEl = itemGroup.childAt(itemGroup.bodyIndex); + bodyEl.style.set(itemStyle); + bodyEl.style.stroke = borderColor; + bodyEl.dirty(); + + var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + graphic.setHoverStyle(itemGroup, hoverStyle); + } + + module.exports = BoxplotView; + + + +/***/ }, +/* 249 */ +/***/ function(module, exports) { + + + + var borderColorQuery = ['itemStyle', 'normal', 'borderColor']; + + module.exports = function (ecModel, api) { + + var globalColors = ecModel.get('color'); + + ecModel.eachRawSeriesByType('boxplot', function (seriesModel) { + + var defaulColor = globalColors[seriesModel.seriesIndex % globalColors.length]; + var data = seriesModel.getData(); + + data.setVisual({ + legendSymbol: 'roundRect', + // Use name 'color' but not 'borderColor' for legend usage and + // visual coding from other component like dataRange. + color: seriesModel.get(borderColorQuery) || defaulColor + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + data.setItemVisual( + idx, + {color: itemModel.get(borderColorQuery, true)} + ); + }); + } + }); + + }; + + +/***/ }, +/* 250 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var each = zrUtil.each; + + module.exports = function (ecModel) { + + var groupResult = groupSeriesByAxis(ecModel); + + each(groupResult, function (groupItem) { + var seriesModels = groupItem.seriesModels; + + if (!seriesModels.length) { + return; + } + + calculateBase(groupItem); + + each(seriesModels, function (seriesModel, idx) { + layoutSingleSeries( + seriesModel, + groupItem.boxOffsetList[idx], + groupItem.boxWidthList[idx] + ); + }); + }); + }; + + /** + * Group series by axis. + */ + function groupSeriesByAxis(ecModel) { + var result = []; + var axisList = []; + + ecModel.eachSeriesByType('boxplot', function (seriesModel) { + var baseAxis = seriesModel.getBaseAxis(); + var idx = zrUtil.indexOf(axisList, baseAxis); + + if (idx < 0) { + idx = axisList.length; + axisList[idx] = baseAxis; + result[idx] = {axis: baseAxis, seriesModels: []}; + } + + result[idx].seriesModels.push(seriesModel); + }); + + return result; + } + + /** + * Calculate offset and box width for each series. + */ + function calculateBase(groupItem) { + var extent; + var baseAxis = groupItem.axis; + var seriesModels = groupItem.seriesModels; + var seriesCount = seriesModels.length; + + var boxWidthList = groupItem.boxWidthList = []; + var boxOffsetList = groupItem.boxOffsetList = []; + var boundList = []; + + var bandWidth; + if (baseAxis.type === 'category') { + bandWidth = baseAxis.getBandWidth(); + } + else { + var maxDataCount = 0; + each(seriesModels, function (seriesModel) { + maxDataCount = Math.max(maxDataCount, seriesModel.getData().count()); + }); + extent = baseAxis.getExtent(), + Math.abs(extent[1] - extent[0]) / maxDataCount; + } + + each(seriesModels, function (seriesModel) { + var boxWidthBound = seriesModel.get('boxWidth'); + if (!zrUtil.isArray(boxWidthBound)) { + boxWidthBound = [boxWidthBound, boxWidthBound]; + } + boundList.push([ + parsePercent(boxWidthBound[0], bandWidth) || 0, + parsePercent(boxWidthBound[1], bandWidth) || 0 + ]); + }); + + var availableWidth = bandWidth * 0.8 - 2; + var boxGap = availableWidth / seriesCount * 0.3; + var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount; + var base = boxWidth / 2 - availableWidth / 2; + + each(seriesModels, function (seriesModel, idx) { + boxOffsetList.push(base); + base += boxGap + boxWidth; + + boxWidthList.push( + Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1]) + ); + }); + } + + /** + * Calculate points location for each series. + */ + function layoutSingleSeries(seriesModel, offset, boxWidth) { + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var dimensions = seriesModel.dimensions; + var chartLayout = seriesModel.get('layout'); + var halfWidth = boxWidth / 2; + + data.each(dimensions, function () { + var args = arguments; + var dimLen = dimensions.length; + var axisDimVal = args[0]; + var idx = args[dimLen]; + var variableDim = chartLayout === 'horizontal' ? 0 : 1; + var constDim = 1 - variableDim; + + var median = getPoint(args[3]); + var end1 = getPoint(args[1]); + var end5 = getPoint(args[5]); + var whiskerEnds = [ + [end1, getPoint(args[2])], + [end5, getPoint(args[4])] + ]; + layEndLine(end1); + layEndLine(end5); + layEndLine(median); + + var bodyEnds = []; + addBodyEnd(whiskerEnds[0][1], 0); + addBodyEnd(whiskerEnds[1][1], 1); + + data.setItemLayout(idx, { + chartLayout: chartLayout, + initBaseline: median[constDim], + median: median, + bodyEnds: bodyEnds, + whiskerEnds: whiskerEnds + }); + + function getPoint(val) { + var p = []; + p[variableDim] = axisDimVal; + p[constDim] = val; + var point; + if (isNaN(axisDimVal) || isNaN(val)) { + point = [NaN, NaN]; + } + else { + point = coordSys.dataToPoint(p); + point[variableDim] += offset; + } + return point; + } + + function addBodyEnd(point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[variableDim] += halfWidth; + point2[variableDim] -= halfWidth; + start + ? bodyEnds.push(point1, point2) + : bodyEnds.push(point2, point1); + } + + function layEndLine(endCenter) { + var line = [endCenter.slice(), endCenter.slice()]; + line[0][variableDim] -= halfWidth; + line[1][variableDim] += halfWidth; + whiskerEnds.push(line); + } + }); + } + + + +/***/ }, +/* 251 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(252); + __webpack_require__(253); + + echarts.registerPreprocessor( + __webpack_require__(254) + ); + + echarts.registerVisual(__webpack_require__(255)); + echarts.registerLayout(__webpack_require__(256)); + + + +/***/ }, +/* 252 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var whiskerBoxCommon = __webpack_require__(246); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var CandlestickSeries = SeriesModel.extend({ + + type: 'series.candlestick', + + dependencies: ['xAxis', 'yAxis', 'grid'], + + /** + * @readOnly + */ + valueDimensions: ['open', 'close', 'lowest', 'highest'], + + /** + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * @override + */ + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + + // xAxisIndex: 0, + // yAxisIndex: 0, + + layout: null, // 'horizontal' or 'vertical' + + itemStyle: { + normal: { + color: '#c23531', // 阳线 positive + color0: '#314656', // 阴线 negative '#c23531', '#314656' + borderWidth: 1, + // FIXME + // ec2中使用的是lineStyle.color 和 lineStyle.color0 + borderColor: '#c23531', + borderColor0: '#314656' + }, + emphasis: { + borderWidth: 2 + } + }, + + animationUpdate: false, + animationEasing: 'linear', + animationDuration: 300 + }, + + /** + * Get dimension for shadow in dataZoom + * @return {string} dimension name + */ + getShadowDim: function () { + return 'open'; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, mutipleSeries) { + // It rearly use mutiple candlestick series in one cartesian, + // so only consider one series in this default tooltip. + var valueHTMLArr = zrUtil.map(this.valueDimensions, function (dim) { + return dim + ': ' + addCommas(this._data.get(dim, dataIndex)); + }, this); + + return encodeHTML(this.name) + '
' + valueHTMLArr.join('
'); + }, + + brushSelector: function (itemLayout, selectors) { + return selectors.rect(itemLayout.brushRect); + } + + }); + + zrUtil.mixin(CandlestickSeries, whiskerBoxCommon.seriesModelMixin, true); + + module.exports = CandlestickSeries; + + + +/***/ }, +/* 253 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + var whiskerBoxCommon = __webpack_require__(246); + + var CandlestickView = ChartView.extend({ + + type: 'candlestick', + + getStyleUpdater: function () { + return updateStyle; + } + + }); + + zrUtil.mixin(CandlestickView, whiskerBoxCommon.viewMixin, true); + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + + function updateStyle(itemGroup, data, idx) { + var itemModel = data.getItemModel(idx); + var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath); + var color = data.getItemVisual(idx, 'color'); + var borderColor = data.getItemVisual(idx, 'borderColor') || color; + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var itemStyle = normalItemStyleModel.getItemStyle( + ['color', 'color0', 'borderColor', 'borderColor0'] + ); + + var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex); + whiskerEl.useStyle(itemStyle); + whiskerEl.style.stroke = borderColor; + + var bodyEl = itemGroup.childAt(itemGroup.bodyIndex); + bodyEl.useStyle(itemStyle); + bodyEl.style.fill = color; + bodyEl.style.stroke = borderColor; + + var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + graphic.setHoverStyle(itemGroup, hoverStyle); + } + + + module.exports = CandlestickView; + + + +/***/ }, +/* 254 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + if (!option || !zrUtil.isArray(option.series)) { + return; + } + + // Translate 'k' to 'candlestick'. + zrUtil.each(option.series, function (seriesItem) { + if (zrUtil.isObject(seriesItem) && seriesItem.type === 'k') { + seriesItem.type = 'candlestick'; + } + }); + }; + + + +/***/ }, +/* 255 */ +/***/ function(module, exports) { + + + + var positiveBorderColorQuery = ['itemStyle', 'normal', 'borderColor']; + var negativeBorderColorQuery = ['itemStyle', 'normal', 'borderColor0']; + var positiveColorQuery = ['itemStyle', 'normal', 'color']; + var negativeColorQuery = ['itemStyle', 'normal', 'color0']; + + module.exports = function (ecModel, api) { + + ecModel.eachRawSeriesByType('candlestick', function (seriesModel) { + + var data = seriesModel.getData(); + + data.setVisual({ + legendSymbol: 'roundRect' + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var sign = data.getItemLayout(idx).sign; + + data.setItemVisual( + idx, + { + color: itemModel.get( + sign > 0 ? positiveColorQuery : negativeColorQuery + ), + borderColor: itemModel.get( + sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery + ) + } + ); + }); + } + }); + + }; + + +/***/ }, +/* 256 */ +/***/ function(module, exports) { + + + + var CANDLE_MIN_WIDTH = 2; + var CANDLE_MIN_NICE_WIDTH = 5; + var GPA_MIN = 4; + + module.exports = function (ecModel) { + + ecModel.eachSeriesByType('candlestick', function (seriesModel) { + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var dimensions = seriesModel.dimensions; + var chartLayout = seriesModel.get('layout'); + + var candleWidth = calculateCandleWidth(seriesModel, data); + + data.each(dimensions, function () { + var args = arguments; + var dimLen = dimensions.length; + var axisDimVal = args[0]; + var idx = args[dimLen]; + var variableDim = chartLayout === 'horizontal' ? 0 : 1; + var constDim = 1 - variableDim; + + var openVal = args[1]; + var closeVal = args[2]; + var lowestVal = args[3]; + var highestVal = args[4]; + + var ocLow = Math.min(openVal, closeVal); + var ocHigh = Math.max(openVal, closeVal); + + var ocLowPoint = getPoint(ocLow); + var ocHighPoint = getPoint(ocHigh); + var lowestPoint = getPoint(lowestVal); + var highestPoint = getPoint(highestVal); + + var whiskerEnds = [ + [highestPoint, ocHighPoint], + [lowestPoint, ocLowPoint] + ]; + + var bodyEnds = []; + addBodyEnd(ocHighPoint, 0); + addBodyEnd(ocLowPoint, 1); + + data.setItemLayout(idx, { + chartLayout: chartLayout, + sign: openVal > closeVal ? -1 : openVal < closeVal ? 1 : 0, + initBaseline: openVal > closeVal + ? ocHighPoint[constDim] : ocLowPoint[constDim], // open point. + bodyEnds: bodyEnds, + whiskerEnds: whiskerEnds, + brushRect: makeBrushRect() + }); + + function getPoint(val) { + var p = []; + p[variableDim] = axisDimVal; + p[constDim] = val; + return (isNaN(axisDimVal) || isNaN(val)) + ? [NaN, NaN] + : coordSys.dataToPoint(p); + } + + function addBodyEnd(point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[variableDim] += candleWidth / 2; + point2[variableDim] -= candleWidth / 2; + start + ? bodyEnds.push(point1, point2) + : bodyEnds.push(point2, point1); + } + + function makeBrushRect() { + var pmin = getPoint(Math.min(openVal, closeVal, lowestVal, highestVal)); + var pmax = getPoint(Math.max(openVal, closeVal, lowestVal, highestVal)); + + pmin[variableDim] -= candleWidth / 2; + pmax[variableDim] -= candleWidth / 2; + + return { + x: pmin[0], + y: pmin[1], + width: constDim ? candleWidth : pmax[0] - pmin[0], + height: constDim ? pmax[1] - pmin[1] : candleWidth + }; + } + + }, true); + }); + }; + + function calculateCandleWidth(seriesModel, data) { + var baseAxis = seriesModel.getBaseAxis(); + var extent; + + var bandWidth = baseAxis.type === 'category' + ? baseAxis.getBandWidth() + : ( + extent = baseAxis.getExtent(), + Math.abs(extent[1] - extent[0]) / data.count() + ); + + // Half band width is perfect when space is enouph, otherwise + // try not to be smaller than CANDLE_MIN_NICE_WIDTH (and only + // gap is compressed), otherwise ensure not to be smaller than + // CANDLE_MIN_WIDTH in spite of overlap. + + return bandWidth / 2 - 2 > CANDLE_MIN_NICE_WIDTH // "- 2" is minus border width + ? bandWidth / 2 - 2 + : bandWidth - CANDLE_MIN_NICE_WIDTH > GPA_MIN + ? CANDLE_MIN_NICE_WIDTH + : Math.max(bandWidth - GPA_MIN, CANDLE_MIN_WIDTH); + } + + + +/***/ }, +/* 257 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(258); + __webpack_require__(259); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'effectScatter', 'circle', null + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'effectScatter' + )); + + +/***/ }, +/* 258 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.effectScatter', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + var list = createListFromArray(option.data, this, ecModel); + return list; + }, + + brushSelector: 'point', + + defaultOption: { + coordinateSystem: 'cartesian2d', + zlevel: 0, + z: 2, + legendHoverLink: true, + + effectType: 'ripple', + + // When to show the effect, option: 'render'|'emphasis' + showEffectOn: 'render', + + // Ripple effect config + rippleEffect: { + period: 4, + // Scale of ripple + scale: 2.5, + // Brush type can be fill or stroke + brushType: 'fill' + }, + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Polar coordinate system + // polarIndex: 0, + + // Geo coordinate system + // geoIndex: 0, + + // symbol: null, // 图形类型 + symbolSize: 10 // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2 + // symbolRotate: null, // 图形旋转控制 + + // large: false, + // Available when large is true + // largeThreshold: 2000, + + // itemStyle: { + // normal: { + // opacity: 1 + // } + // } + } + + }); + + +/***/ }, +/* 259 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var EffectSymbol = __webpack_require__(260); + + __webpack_require__(1).extendChartView({ + + type: 'effectScatter', + + init: function () { + this._symbolDraw = new SymbolDraw(EffectSymbol); + }, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var effectSymbolDraw = this._symbolDraw; + effectSymbolDraw.updateData(data); + this.group.add(effectSymbolDraw.group); + }, + + updateLayout: function () { + this._symbolDraw.updateLayout(); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(api); + } + }); + + +/***/ }, +/* 260 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Symbol with ripple effect + * @module echarts/chart/helper/EffectSymbol + */ + + + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + var Symbol = __webpack_require__(105); + var Group = graphic.Group; + + var EFFECT_RIPPLE_NUMBER = 3; + + function normalizeSymbolSize(symbolSize) { + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + + function updateRipplePath(rippleGroup, effectCfg) { + rippleGroup.eachChild(function (ripplePath) { + ripplePath.attr({ + z: effectCfg.z, + zlevel: effectCfg.zlevel, + style: { + stroke: effectCfg.brushType === 'stroke' ? effectCfg.color : null, + fill: effectCfg.brushType === 'fill' ? effectCfg.color : null + } + }); + }); + } + /** + * @constructor + * @param {module:echarts/data/List} data + * @param {number} idx + * @extends {module:zrender/graphic/Group} + */ + function EffectSymbol(data, idx) { + Group.call(this); + + var symbol = new Symbol(data, idx); + var rippleGroup = new Group(); + this.add(symbol); + this.add(rippleGroup); + + rippleGroup.beforeUpdate = function () { + this.attr(symbol.getScale()); + }; + this.updateData(data, idx); + } + + var effectSymbolProto = EffectSymbol.prototype; + + effectSymbolProto.stopEffectAnimation = function () { + this.childAt(1).removeAll(); + }; + + effectSymbolProto.startEffectAnimation = function (effectCfg) { + var symbolType = effectCfg.symbolType; + var color = effectCfg.color; + var rippleGroup = this.childAt(1); + + for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) { + var ripplePath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + ripplePath.attr({ + style: { + strokeNoScale: true + }, + z2: 99, + silent: true, + scale: [1, 1] + }); + + var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; + // TODO Configurable effectCfg.period + ripplePath.animate('', true) + .when(effectCfg.period, { + scale: [effectCfg.rippleScale, effectCfg.rippleScale] + }) + .delay(delay) + .start(); + ripplePath.animateStyle(true) + .when(effectCfg.period, { + opacity: 0 + }) + .delay(delay) + .start(); + + rippleGroup.add(ripplePath); + } + + updateRipplePath(rippleGroup, effectCfg); + }; + + /** + * Update effect symbol + */ + effectSymbolProto.updateEffectAnimation = function (effectCfg) { + var oldEffectCfg = this._effectCfg; + var rippleGroup = this.childAt(1); + + // Must reinitialize effect if following configuration changed + var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale']; + for (var i = 0; i < DIFFICULT_PROPS; i++) { + var propName = DIFFICULT_PROPS[i]; + if (oldEffectCfg[propName] !== effectCfg[propName]) { + this.stopEffectAnimation(); + this.startEffectAnimation(effectCfg); + return; + } + } + + updateRipplePath(rippleGroup, effectCfg); + }; + + /** + * Highlight symbol + */ + effectSymbolProto.highlight = function () { + this.trigger('emphasis'); + }; + + /** + * Downplay symbol + */ + effectSymbolProto.downplay = function () { + this.trigger('normal'); + }; + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + effectSymbolProto.updateData = function (data, idx) { + var seriesModel = data.hostModel; + + this.childAt(0).updateData(data, idx); + + var rippleGroup = this.childAt(1); + var itemModel = data.getItemModel(idx); + var symbolType = data.getItemVisual(idx, 'symbol'); + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + var color = data.getItemVisual(idx, 'color'); + + rippleGroup.attr('scale', symbolSize); + + rippleGroup.traverse(function (ripplePath) { + ripplePath.attr({ + fill: color + }); + }); + + var symbolOffset = itemModel.getShallow('symbolOffset'); + if (symbolOffset) { + var pos = rippleGroup.position; + pos[0] = numberUtil.parsePercent(symbolOffset[0], symbolSize[0]); + pos[1] = numberUtil.parsePercent(symbolOffset[1], symbolSize[1]); + } + rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0; + + var effectCfg = {}; + + effectCfg.showEffectOn = seriesModel.get('showEffectOn'); + effectCfg.rippleScale = itemModel.get('rippleEffect.scale'); + effectCfg.brushType = itemModel.get('rippleEffect.brushType'); + effectCfg.period = itemModel.get('rippleEffect.period') * 1000; + effectCfg.effectOffset = idx / data.count(); + effectCfg.z = itemModel.getShallow('z') || 0; + effectCfg.zlevel = itemModel.getShallow('zlevel') || 0; + effectCfg.symbolType = symbolType; + effectCfg.color = color; + + this.off('mouseover').off('mouseout').off('emphasis').off('normal'); + + if (effectCfg.showEffectOn === 'render') { + this._effectCfg + ? this.updateEffectAnimation(effectCfg) + : this.startEffectAnimation(effectCfg); + + this._effectCfg = effectCfg; + } + else { + // Not keep old effect config + this._effectCfg = null; + + this.stopEffectAnimation(); + var symbol = this.childAt(0); + var onEmphasis = function () { + symbol.trigger('emphasis'); + if (effectCfg.showEffectOn !== 'render') { + this.startEffectAnimation(effectCfg); + } + }; + var onNormal = function () { + symbol.trigger('normal'); + if (effectCfg.showEffectOn !== 'render') { + this.stopEffectAnimation(); + } + }; + this.on('mouseover', onEmphasis, this) + .on('mouseout', onNormal, this) + .on('emphasis', onEmphasis, this) + .on('normal', onNormal, this); + } + + this._effectCfg = effectCfg; + }; + + effectSymbolProto.fadeOut = function (cb) { + this.off('mouseover').off('mouseout').off('emphasis').off('normal'); + cb && cb(); + }; + + zrUtil.inherits(EffectSymbol, Group); + + module.exports = EffectSymbol; + + +/***/ }, +/* 261 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(262); + __webpack_require__(263); + + var echarts = __webpack_require__(1); + echarts.registerLayout( + __webpack_require__(268) + ); + + +/***/ }, +/* 262 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var CoordinateSystem = __webpack_require__(26); + + // Convert [ [{coord: []}, {coord: []}] ] + // to [ { coords: [[]] } ] + function preprocessOption (seriesOpt) { + var data = seriesOpt.data; + if (data && data[0] && data[0][0] && data[0][0].coord) { + if (true) { + console.warn('Lines data configuration has been changed to' + + ' { coords:[[1,2],[2,3]] }'); + } + seriesOpt.data = zrUtil.map(data, function (itemOpt) { + var coords = [ + itemOpt[0].coord, itemOpt[1].coord + ]; + var target = { + coords: coords + }; + if (itemOpt[0].name) { + target.fromName = itemOpt[0].name; + } + if (itemOpt[1].name) { + target.toName = itemOpt[1].name; + } + return zrUtil.mergeAll([target, itemOpt[0], itemOpt[1]]); + }); + } + } + + var LinesSeries = SeriesModel.extend({ + + type: 'series.lines', + + dependencies: ['grid', 'polar'], + + visualColorAccessPath: 'lineStyle.normal.color', + + init: function (option) { + // Not using preprocessor because mergeOption may not have series.type + preprocessOption(option); + + LinesSeries.superApply(this, 'init', arguments); + }, + + mergeOption: function (option) { + preprocessOption(option); + + LinesSeries.superApply(this, 'mergeOption', arguments); + }, + + getInitialData: function (option, ecModel) { + if (true) { + var CoordSys = CoordinateSystem.get(option.coordinateSystem); + if (!CoordSys) { + throw new Error('Unkown coordinate system ' + option.coordinateSystem); + } + } + + var lineData = new List(['value'], this); + lineData.hasItemOption = false; + lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) { + // dataItem is simply coords + if (dataItem instanceof Array) { + return NaN; + } + else { + lineData.hasItemOption = true; + var value = dataItem.value; + if (value) { + return value instanceof Array ? value[dimIndex] : value; + } + } + }); + + return lineData; + }, + + formatTooltip: function (dataIndex) { + var data = this.getData(); + var itemModel = data.getItemModel(dataIndex); + var name = itemModel.get('name'); + if (name) { + return name; + } + var fromName = itemModel.get('fromName'); + var toName = itemModel.get('toName'); + return fromName + ' > ' + toName; + }, + + defaultOption: { + coordinateSystem: 'geo', + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // Cartesian coordinate system + xAxisIndex: 0, + yAxisIndex: 0, + + // Geo coordinate system + geoIndex: 0, + + effect: { + show: false, + period: 4, + // Animation delay. support callback + // delay: 0, + // If move with constant speed px/sec + // period will be ignored if this property is > 0, + constantSpeed: 0, + symbol: 'circle', + symbolSize: 3, + loop: true, + // Length of trail, 0 - 1 + trailLength: 0.2 + // Same with lineStyle.normal.color + // color + }, + + large: false, + // Available when large is true + largeThreshold: 2000, + + // If lines are polyline + // polyline not support curveness, label, animation + polyline: false, + + label: { + normal: { + show: false, + position: 'end' + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + } + }, + + lineStyle: { + normal: { + opacity: 0.5 + } + } + } + }); + + +/***/ }, +/* 263 */ +/***/ function(module, exports, __webpack_require__) { + + + + var LineDraw = __webpack_require__(199); + var EffectLine = __webpack_require__(264); + var Line = __webpack_require__(200); + var Polyline = __webpack_require__(265); + var EffectPolyline = __webpack_require__(266); + var LargeLineDraw = __webpack_require__(267); + + __webpack_require__(1).extendChartView({ + + type: 'lines', + + init: function () {}, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var lineDraw = this._lineDraw; + + var hasEffect = seriesModel.get('effect.show'); + var isPolyline = seriesModel.get('polyline'); + var isLarge = seriesModel.get('large') && data.count() >= seriesModel.get('largeThreshold'); + + if (true) { + if (hasEffect && isLarge) { + console.warn('Large lines not support effect'); + } + } + if (hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLarge !== this._isLarge) { + if (lineDraw) { + lineDraw.remove(); + } + lineDraw = this._lineDraw = isLarge + ? new LargeLineDraw() + : new LineDraw( + isPolyline + ? (hasEffect ? EffectPolyline : Polyline) + : (hasEffect ? EffectLine : Line) + ); + this._hasEffet = hasEffect; + this._isPolyline = isPolyline; + this._isLarge = isLarge; + } + + var zlevel = seriesModel.get('zlevel'); + var trailLength = seriesModel.get('effect.trailLength'); + + var zr = api.getZr(); + // Avoid the drag cause ghost shadow + // FIXME Better way ? + zr.painter.getLayer(zlevel).clear(true); + // Config layer with motion blur + if (this._lastZlevel != null) { + zr.configLayer(this._lastZlevel, { + motionBlur: false + }); + } + if (hasEffect && trailLength) { + if (true) { + var notInIndividual = false; + ecModel.eachSeries(function (otherSeriesModel) { + if (otherSeriesModel !== seriesModel && otherSeriesModel.get('zlevel') === zlevel) { + notInIndividual = true; + } + }); + notInIndividual && console.warn('Lines with trail effect should have an individual zlevel'); + } + + zr.configLayer(zlevel, { + motionBlur: true, + lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) + }); + } + + this.group.add(lineDraw.group); + + lineDraw.updateData(data); + + this._lastZlevel = zlevel; + }, + + updateLayout: function (seriesModel, ecModel, api) { + this._lineDraw.updateLayout(seriesModel); + // Not use motion when dragging or zooming + var zr = api.getZr(); + zr.painter.getLayer(this._lastZlevel).clear(true); + }, + + remove: function (ecModel, api) { + this._lineDraw && this._lineDraw.remove(api, true); + } + }); + + +/***/ }, +/* 264 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + + + var graphic = __webpack_require__(43); + var Line = __webpack_require__(200); + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var vec2 = __webpack_require__(10); + + var curveUtil = __webpack_require__(50); + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Line} + */ + function EffectLine(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this.add(this.createLine(lineData, idx, seriesScope)); + + this._updateEffectSymbol(lineData, idx); + } + + var effectLineProto = EffectLine.prototype; + + effectLineProto.createLine = function (lineData, idx, seriesScope) { + return new Line(lineData, idx, seriesScope); + }; + + effectLineProto._updateEffectSymbol = function (lineData, idx) { + var itemModel = lineData.getItemModel(idx); + var effectModel = itemModel.getModel('effect'); + var size = effectModel.get('symbolSize'); + var symbolType = effectModel.get('symbol'); + if (!zrUtil.isArray(size)) { + size = [size, size]; + } + var color = effectModel.get('color') || lineData.getItemVisual(idx, 'color'); + var symbol = this.childAt(1); + + if (this._symbolType !== symbolType) { + // Remove previous + this.remove(symbol); + + symbol = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + symbol.z2 = 100; + symbol.culling = true; + + this.add(symbol); + } + + // Symbol may be removed if loop is false + if (!symbol) { + return; + } + + // Shadow color is same with color in default + symbol.setStyle('shadowColor', color); + symbol.setStyle(effectModel.getItemStyle(['color'])); + + symbol.attr('scale', size); + + symbol.setColor(color); + symbol.attr('scale', size); + + this._symbolType = symbolType; + + this._updateEffectAnimation(lineData, effectModel, idx); + }; + + effectLineProto._updateEffectAnimation = function (lineData, effectModel, idx) { + + var symbol = this.childAt(1); + if (!symbol) { + return; + } + + var self = this; + + var points = lineData.getItemLayout(idx); + + var period = effectModel.get('period') * 1000; + var loop = effectModel.get('loop'); + var constantSpeed = effectModel.get('constantSpeed'); + var delayExpr = effectModel.get('delay') || function (idx) { + return idx / lineData.count() * period / 3; + }; + var isDelayFunc = typeof delayExpr === 'function'; + + // Ignore when updating + symbol.ignore = true; + + this.updateAnimationPoints(symbol, points); + + if (constantSpeed > 0) { + period = this.getLineLength(symbol) / constantSpeed * 1000; + } + + if (period !== this._period || loop !== this._loop) { + + symbol.stopAnimation(); + + var delay = delayExpr; + if (isDelayFunc) { + delay = delayExpr(idx); + } + if (symbol.__t > 0) { + delay = -period * symbol.__t; + } + symbol.__t = 0; + var animator = symbol.animate('', loop) + .when(period, { + __t: 1 + }) + .delay(delay) + .during(function () { + self.updateSymbolPosition(symbol); + }); + if (!loop) { + animator.done(function () { + self.remove(symbol); + }); + } + animator.start(); + } + + this._period = period; + this._loop = loop; + }; + + effectLineProto.getLineLength = function (symbol) { + // Not so accurate + return (vec2.dist(symbol.__p1, symbol.__cp1) + + vec2.dist(symbol.__cp1, symbol.__p2)); + }; + + effectLineProto.updateAnimationPoints = function (symbol, points) { + symbol.__p1 = points[0]; + symbol.__p2 = points[1]; + symbol.__cp1 = points[2] || [ + (points[0][0] + points[1][0]) / 2, + (points[0][1] + points[1][1]) / 2 + ]; + }; + + effectLineProto.updateData = function (lineData, idx, seriesScope) { + this.childAt(0).updateData(lineData, idx, seriesScope); + this._updateEffectSymbol(lineData, idx); + }; + + effectLineProto.updateSymbolPosition = function (symbol) { + var p1 = symbol.__p1; + var p2 = symbol.__p2; + var cp1 = symbol.__cp1; + var t = symbol.__t; + var pos = symbol.position; + var quadraticAt = curveUtil.quadraticAt; + var quadraticDerivativeAt = curveUtil.quadraticDerivativeAt; + pos[0] = quadraticAt(p1[0], cp1[0], p2[0], t); + pos[1] = quadraticAt(p1[1], cp1[1], p2[1], t); + + // Tangent + var tx = quadraticDerivativeAt(p1[0], cp1[0], p2[0], t); + var ty = quadraticDerivativeAt(p1[1], cp1[1], p2[1], t); + + symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; + + symbol.ignore = false; + }; + + + effectLineProto.updateLayout = function (lineData, idx) { + this.childAt(0).updateLayout(lineData, idx); + + var effectModel = lineData.getItemModel(idx).getModel('effect'); + this._updateEffectAnimation(lineData, effectModel, idx); + }; + + zrUtil.inherits(EffectLine, graphic.Group); + + module.exports = EffectLine; + + +/***/ }, +/* 265 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Line + */ + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Polyline} + */ + function Polyline(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this._createPolyline(lineData, idx, seriesScope); + } + + var polylineProto = Polyline.prototype; + + polylineProto._createPolyline = function (lineData, idx, seriesScope) { + // var seriesModel = lineData.hostModel; + var points = lineData.getItemLayout(idx); + + var line = new graphic.Polyline({ + shape: { + points: points + } + }); + + this.add(line); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + polylineProto.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childAt(0); + var target = { + shape: { + points: lineData.getItemLayout(idx) + } + }; + graphic.updateProps(line, target, seriesModel, idx); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + polylineProto._updateCommonStl = function (lineData, idx, seriesScope) { + var line = this.childAt(0); + var itemModel = lineData.getItemModel(idx); + + var visualColor = lineData.getItemVisual(idx, 'color'); + + var lineStyle = seriesScope && seriesScope.lineStyle; + var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; + + if (!seriesScope || lineData.hasItemOption) { + lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle(); + hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + } + line.useStyle(zrUtil.defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + graphic.setHoverStyle(this); + }; + + polylineProto.updateLayout = function (lineData, idx) { + var polyline = this.childAt(0); + polyline.setShape('points', lineData.getItemLayout(idx)); + }; + + zrUtil.inherits(Polyline, graphic.Group); + + module.exports = Polyline; + + +/***/ }, +/* 266 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + + + var Polyline = __webpack_require__(265); + var zrUtil = __webpack_require__(4); + var EffectLine = __webpack_require__(264); + var vec2 = __webpack_require__(10); + + /** + * @constructor + * @extends {module:echarts/chart/helper/EffectLine} + * @alias {module:echarts/chart/helper/Polyline} + */ + function EffectPolyline(lineData, idx, seriesScope) { + EffectLine.call(this, lineData, idx, seriesScope); + this._lastFrame = 0; + this._lastFramePercent = 0; + } + + var effectPolylineProto = EffectPolyline.prototype; + + // Overwrite + effectPolylineProto.createLine = function (lineData, idx, seriesScope) { + return new Polyline(lineData, idx, seriesScope); + }; + + // Overwrite + effectPolylineProto.updateAnimationPoints = function (symbol, points) { + this._points = points; + var accLenArr = [0]; + var len = 0; + for (var i = 1; i < points.length; i++) { + var p1 = points[i - 1]; + var p2 = points[i]; + len += vec2.dist(p1, p2); + accLenArr.push(len); + } + if (len === 0) { + return; + } + + for (var i = 0; i < accLenArr.length; i++) { + accLenArr[i] /= len; + } + this._offsets = accLenArr; + this._length = len; + }; + + // Overwrite + effectPolylineProto.getLineLength = function (symbol) { + return this._length; + }; + + // Overwrite + effectPolylineProto.updateSymbolPosition = function (symbol) { + var t = symbol.__t; + var points = this._points; + var offsets = this._offsets; + var len = points.length; + + if (!offsets) { + // Has length 0 + return; + } + + var lastFrame = this._lastFrame; + var frame; + + if (t < this._lastFramePercent) { + // Start from the next frame + // PENDING start from lastFrame ? + var start = Math.min(lastFrame + 1, len - 1); + for (frame = start; frame >= 0; frame--) { + if (offsets[frame] <= t) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, len - 2); + } + else { + for (var frame = lastFrame; frame < len; frame++) { + if (offsets[frame] > t) { + break; + } + } + frame = Math.min(frame - 1, len - 2); + } + + vec2.lerp( + symbol.position, points[frame], points[frame + 1], + (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]) + ); + + this._lastFrame = frame; + this._lastFramePercent = t; + + symbol.ignore = false; + }; + + zrUtil.inherits(EffectPolyline, EffectLine); + + module.exports = EffectPolyline; + + +/***/ }, +/* 267 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Batch by color + + + + var graphic = __webpack_require__(43); + + var quadraticContain = __webpack_require__(55); + var lineContain = __webpack_require__(53); + + var LargeLineShape = graphic.extendShape({ + shape: { + polyline: false, + + segs: [] + }, + + buildPath: function (path, shape) { + var segs = shape.segs; + var isPolyline = shape.polyline; + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (isPolyline) { + path.moveTo(seg[0][0], seg[0][1]); + for (var j = 1; j < seg.length; j++) { + path.lineTo(seg[j][0], seg[j][1]); + } + } + else { + path.moveTo(seg[0][0], seg[0][1]); + if (seg.length > 2) { + path.quadraticCurveTo(seg[2][0], seg[2][1], seg[1][0], seg[1][1]); + } + else { + path.lineTo(seg[1][0], seg[1][1]); + } + } + } + }, + + findDataIndex: function (x, y) { + var shape = this.shape; + var segs = shape.segs; + var isPolyline = shape.polyline; + var lineWidth = Math.max(this.style.lineWidth, 1); + + // Not consider transform + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (isPolyline) { + for (var j = 1; j < seg.length; j++) { + if (lineContain.containStroke( + seg[j - 1][0], seg[j - 1][1], seg[j][0], seg[j][1], lineWidth, x, y + )) { + return i; + } + } + } + else { + if (seg.length > 2) { + if (quadraticContain.containStroke( + seg[0][0], seg[0][1], seg[2][0], seg[2][1], seg[1][0], seg[1][1], lineWidth, x, y + )) { + return i; + } + } + else { + if (lineContain.containStroke( + seg[0][0], seg[0][1], seg[1][0], seg[1][1], lineWidth, x, y + )) { + return i; + } + } + } + } + + return -1; + } + }); + + function LargeLineDraw() { + this.group = new graphic.Group(); + + this._lineEl = new LargeLineShape(); + } + + var largeLineProto = LargeLineDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + largeLineProto.updateData = function (data) { + this.group.removeAll(); + + var lineEl = this._lineEl; + + var seriesModel = data.hostModel; + + lineEl.setShape({ + segs: data.mapArray(data.getItemLayout), + polyline: seriesModel.get('polyline') + }); + + lineEl.useStyle( + seriesModel.getModel('lineStyle.normal').getLineStyle() + ); + + var visualColor = data.getVisual('color'); + if (visualColor) { + lineEl.setStyle('stroke', visualColor); + } + lineEl.setStyle('fill'); + + // Enable tooltip + // PENDING May have performance issue when path is extremely large + lineEl.seriesIndex = seriesModel.seriesIndex; + lineEl.on('mousemove', function (e) { + lineEl.dataIndex = null; + var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY); + if (dataIndex > 0) { + // Provide dataIndex for tooltip + lineEl.dataIndex = dataIndex; + } + }); + + // Add back + this.group.add(lineEl); + }; + + largeLineProto.updateLayout = function (seriesModel) { + var data = seriesModel.getData(); + this._lineEl.setShape({ + segs: data.mapArray(data.getItemLayout) + }); + }; + + largeLineProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LargeLineDraw; + + +/***/ }, +/* 268 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('lines', function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var lineData = seriesModel.getData(); + + // FIXME Use data dimensions ? + lineData.each(function (idx) { + var itemModel = lineData.getItemModel(idx); + // TODO Support pure array + var coords = (itemModel.option instanceof Array) ? + itemModel.option : itemModel.get('coords'); + + if (true) { + if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) { + throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.'); + } + } + var pts = []; + + if (seriesModel.get('polyline')) { + for (var i = 0; i < coords.length; i++) { + pts.push(coordSys.dataToPoint(coords[i])); + } + } + else { + pts[0] = coordSys.dataToPoint(coords[0]); + pts[1] = coordSys.dataToPoint(coords[1]); + + var curveness = itemModel.get('lineStyle.normal.curveness'); + if (+curveness) { + pts[2] = [ + (pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, + (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness + ]; + } + } + lineData.setItemLayout(idx, pts); + }); + }); + }; + + +/***/ }, +/* 269 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(270); + __webpack_require__(271); + + +/***/ }, +/* 270 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SeriesModel = __webpack_require__(28); + var createListFromArray = __webpack_require__(101); + + module.exports = SeriesModel.extend({ + type: 'series.heatmap', + + getInitialData: function (option, ecModel) { + return createListFromArray(option.data, this, ecModel); + }, + + defaultOption: { + + // Cartesian2D or geo + coordinateSystem: 'cartesian2d', + + zlevel: 0, + + z: 2, + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Geo coordinate system + geoIndex: 0, + + blurSize: 30, + + pointSize: 20, + + maxOpacity: 1, + + minOpacity: 0 + } + }); + + +/***/ }, +/* 271 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var HeatmapLayer = __webpack_require__(272); + var zrUtil = __webpack_require__(4); + + function getIsInPiecewiseRange(dataExtent, pieceList, selected) { + var dataSpan = dataExtent[1] - dataExtent[0]; + pieceList = zrUtil.map(pieceList, function (piece) { + return { + interval: [ + (piece.interval[0] - dataExtent[0]) / dataSpan, + (piece.interval[1] - dataExtent[0]) / dataSpan + ] + }; + }); + var len = pieceList.length; + var lastIndex = 0; + return function (val) { + // Try to find in the location of the last found + for (var i = lastIndex; i < len; i++) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + if (i === len) { // Not found, back interation + for (var i = lastIndex - 1; i >= 0; i--) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + } + return i >= 0 && i < len && selected[i]; + }; + } + + function getIsInContinuousRange(dataExtent, range) { + var dataSpan = dataExtent[1] - dataExtent[0]; + range = [ + (range[0] - dataExtent[0]) / dataSpan, + (range[1] - dataExtent[0]) / dataSpan + ]; + return function (val) { + return val >= range[0] && val <= range[1]; + }; + } + + function isGeoCoordSys(coordSys) { + var dimensions = coordSys.dimensions; + // Not use coorSys.type === 'geo' because coordSys maybe extended + return dimensions[0] === 'lng' && dimensions[1] === 'lat'; + } + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'heatmap', + + render: function (seriesModel, ecModel, api) { + var visualMapOfThisSeries; + ecModel.eachComponent('visualMap', function (visualMap) { + visualMap.eachTargetSeries(function (targetSeries) { + if (targetSeries === seriesModel) { + visualMapOfThisSeries = visualMap; + } + }); + }); + + if (true) { + if (!visualMapOfThisSeries) { + throw new Error('Heatmap must use with visualMap'); + } + } + + this.group.removeAll(); + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type === 'cartesian2d') { + this._renderOnCartesian(coordSys, seriesModel, api); + } + else if (isGeoCoordSys(coordSys)) { + this._renderOnGeo( + coordSys, seriesModel, visualMapOfThisSeries, api + ); + } + }, + + _renderOnCartesian: function (cartesian, seriesModel, api) { + var xAxis = cartesian.getAxis('x'); + var yAxis = cartesian.getAxis('y'); + var group = this.group; + + if (true) { + if (!(xAxis.type === 'category' && yAxis.type === 'category')) { + throw new Error('Heatmap on cartesian must have two category axes'); + } + if (!(xAxis.onBand && yAxis.onBand)) { + throw new Error('Heatmap on cartesian must have two axes with boundaryGap true'); + } + } + + var width = xAxis.getBandWidth(); + var height = yAxis.getBandWidth(); + + var data = seriesModel.getData(); + + var itemStyleQuery = 'itemStyle.normal'; + var hoverItemStyleQuery = 'itemStyle.emphasis'; + var labelQuery = 'label.normal'; + var hoverLabelQuery = 'label.emphasis'; + var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']); + var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle(); + var labelModel = seriesModel.getModel('label.normal'); + var hoverLabelModel = seriesModel.getModel('label.emphasis'); + + data.each(['x', 'y', 'z'], function (x, y, z, idx) { + var itemModel = data.getItemModel(idx); + var point = cartesian.dataToPoint([x, y]); + // Ignore empty data + if (isNaN(z)) { + return; + } + var rect = new graphic.Rect({ + shape: { + x: point[0] - width / 2, + y: point[1] - height / 2, + width: width, + height: height + }, + style: { + fill: data.getItemVisual(idx, 'color'), + opacity: data.getItemVisual(idx, 'opacity') + } + }); + // Optimization for large datset + if (data.hasItemOption) { + style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']); + hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle(); + labelModel = itemModel.getModel(labelQuery); + hoverLabelModel = itemModel.getModel(hoverLabelQuery); + } + + var rawValue = seriesModel.getRawValue(idx); + var defaultText = '-'; + if (rawValue && rawValue[2] != null) { + defaultText = rawValue[2]; + } + if (labelModel.getShallow('show')) { + graphic.setText(style, labelModel); + style.text = seriesModel.getFormattedLabel(idx, 'normal') || defaultText; + } + if (hoverLabelModel.getShallow('show')) { + graphic.setText(hoverStl, hoverLabelModel); + hoverStl.text = seriesModel.getFormattedLabel(idx, 'emphasis') || defaultText; + } + + rect.setStyle(style); + + graphic.setHoverStyle(rect, data.hasItemOption ? hoverStl : zrUtil.extend({}, hoverStl)); + + group.add(rect); + data.setItemGraphicEl(idx, rect); + }); + }, + + _renderOnGeo: function (geo, seriesModel, visualMapModel, api) { + var inRangeVisuals = visualMapModel.targetVisuals.inRange; + var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; + // if (!visualMapping) { + // throw new Error('Data range must have color visuals'); + // } + + var data = seriesModel.getData(); + var hmLayer = this._hmLayer || (this._hmLayer || new HeatmapLayer()); + hmLayer.blurSize = seriesModel.get('blurSize'); + hmLayer.pointSize = seriesModel.get('pointSize'); + hmLayer.minOpacity = seriesModel.get('minOpacity'); + hmLayer.maxOpacity = seriesModel.get('maxOpacity'); + + var rect = geo.getViewRect().clone(); + var roamTransform = geo.getRoamTransform().transform; + rect.applyTransform(roamTransform); + + // Clamp on viewport + var x = Math.max(rect.x, 0); + var y = Math.max(rect.y, 0); + var x2 = Math.min(rect.width + rect.x, api.getWidth()); + var y2 = Math.min(rect.height + rect.y, api.getHeight()); + var width = x2 - x; + var height = y2 - y; + + var points = data.mapArray(['lng', 'lat', 'value'], function (lng, lat, value) { + var pt = geo.dataToPoint([lng, lat]); + pt[0] -= x; + pt[1] -= y; + pt.push(value); + return pt; + }); + + var dataExtent = visualMapModel.getExtent(); + var isInRange = visualMapModel.type === 'visualMap.continuous' + ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) + : getIsInPiecewiseRange( + dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected + ); + + hmLayer.update( + points, width, height, + inRangeVisuals.color.getNormalizer(), + { + inRange: inRangeVisuals.color.getColorMapper(), + outOfRange: outOfRangeVisuals.color.getColorMapper() + }, + isInRange + ); + var img = new graphic.Image({ + style: { + width: width, + height: height, + x: x, + y: y, + image: hmLayer.canvas + }, + silent: true + }); + this.group.add(img); + } + }); + + +/***/ }, +/* 272 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file defines echarts Heatmap Chart + * @author Ovilia (me@zhangwenli.com) + * Inspired by https://github.com/mourner/simpleheat + * + * @module + */ + + + var GRADIENT_LEVELS = 256; + var zrUtil = __webpack_require__(4); + + /** + * Heatmap Chart + * + * @class + */ + function Heatmap() { + var canvas = zrUtil.createCanvas(); + this.canvas = canvas; + + this.blurSize = 30; + this.pointSize = 20; + + this.maxOpacity = 1; + this.minOpacity = 0; + + this._gradientPixels = {}; + } + + Heatmap.prototype = { + /** + * Renders Heatmap and returns the rendered canvas + * @param {Array} data array of data, each has x, y, value + * @param {number} width canvas width + * @param {number} height canvas height + */ + update: function(data, width, height, normalize, colorFunc, isInRange) { + var brush = this._getBrush(); + var gradientInRange = this._getGradient(data, colorFunc, 'inRange'); + var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange'); + var r = this.pointSize + this.blurSize; + + var canvas = this.canvas; + var ctx = canvas.getContext('2d'); + var len = data.length; + canvas.width = width; + canvas.height = height; + for (var i = 0; i < len; ++i) { + var p = data[i]; + var x = p[0]; + var y = p[1]; + var value = p[2]; + + // calculate alpha using value + var alpha = normalize(value); + + // draw with the circle brush with alpha + ctx.globalAlpha = alpha; + ctx.drawImage(brush, x - r, y - r); + } + + // colorize the canvas using alpha value and set with gradient + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var pixels = imageData.data; + var offset = 0; + var pixelLen = pixels.length; + var minOpacity = this.minOpacity; + var maxOpacity = this.maxOpacity; + var diffOpacity = maxOpacity - minOpacity; + + while(offset < pixelLen) { + var alpha = pixels[offset + 3] / 256; + var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; + // Simple optimize to ignore the empty data + if (alpha > 0) { + var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; + // Any alpha > 0 will be mapped to [minOpacity, maxOpacity] + alpha > 0 && (alpha = alpha * diffOpacity + minOpacity); + pixels[offset++] = gradient[gradientOffset]; + pixels[offset++] = gradient[gradientOffset + 1]; + pixels[offset++] = gradient[gradientOffset + 2]; + pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256; + } + else { + offset += 4; + } + } + ctx.putImageData(imageData, 0, 0); + + return canvas; + }, + + /** + * get canvas of a black circle brush used for canvas to draw later + * @private + * @returns {Object} circle brush canvas + */ + _getBrush: function() { + var brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas()); + // set brush size + var r = this.pointSize + this.blurSize; + var d = r * 2; + brushCanvas.width = d; + brushCanvas.height = d; + + var ctx = brushCanvas.getContext('2d'); + ctx.clearRect(0, 0, d, d); + + // in order to render shadow without the distinct circle, + // draw the distinct circle in an invisible place, + // and use shadowOffset to draw shadow in the center of the canvas + ctx.shadowOffsetX = d; + ctx.shadowBlur = this.blurSize; + // draw the shadow in black, and use alpha and shadow blur to generate + // color in color map + ctx.shadowColor = '#000'; + + // draw circle in the left to the canvas + ctx.beginPath(); + ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fill(); + return brushCanvas; + }, + + /** + * get gradient color map + * @private + */ + _getGradient: function (data, colorFunc, state) { + var gradientPixels = this._gradientPixels; + var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4)); + var color = []; + var off = 0; + for (var i = 0; i < 256; i++) { + colorFunc[state](i / 255, true, color); + pixelsSingleState[off++] = color[0]; + pixelsSingleState[off++] = color[1]; + pixelsSingleState[off++] = color[2]; + pixelsSingleState[off++] = color[3]; + } + return pixelsSingleState; + } + }; + + module.exports = Heatmap; + + + +/***/ }, +/* 273 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Legend component entry file8 + */ + + + __webpack_require__(274); + __webpack_require__(275); + __webpack_require__(276); + + var echarts = __webpack_require__(1); + // Series Filter + echarts.registerProcessor(__webpack_require__(278)); + + +/***/ }, +/* 274 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + + var LegendModel = __webpack_require__(1).extendComponentModel({ + + type: 'legend', + + dependencies: ['series'], + + layoutMode: { + type: 'box', + ignoreSize: true + }, + + init: function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + + option.selected = option.selected || {}; + }, + + mergeOption: function (option) { + LegendModel.superCall(this, 'mergeOption', option); + }, + + optionUpdated: function () { + this._updateData(this.ecModel); + + var legendData = this._data; + + // If selectedMode is single, try to select one + if (legendData[0] && this.get('selectedMode') === 'single') { + var hasSelected = false; + // If has any selected in option.selected + for (var i = 0; i < legendData.length; i++) { + var name = legendData[i].get('name'); + if (this.isSelected(name)) { + // Force to unselect others + this.select(name); + hasSelected = true; + break; + } + } + // Try select the first if selectedMode is single + !hasSelected && this.select(legendData[0].get('name')); + } + }, + + _updateData: function (ecModel) { + var legendData = zrUtil.map(this.get('data') || [], function (dataItem) { + // Can be string or number + if (typeof dataItem === 'string' || typeof dataItem === 'number') { + dataItem = { + name: dataItem + }; + } + return new Model(dataItem, this, this.ecModel); + }, this); + this._data = legendData; + + var availableNames = zrUtil.map(ecModel.getSeries(), function (series) { + return series.name; + }); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.legendDataProvider) { + var data = seriesModel.legendDataProvider(); + availableNames = availableNames.concat(data.mapArray(data.getName)); + } + }); + /** + * @type {Array.} + * @private + */ + this._availableNames = availableNames; + }, + + /** + * @return {Array.} + */ + getData: function () { + return this._data; + }, + + /** + * @param {string} name + */ + select: function (name) { + var selected = this.option.selected; + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + var data = this._data; + zrUtil.each(data, function (dataItem) { + selected[dataItem.get('name')] = false; + }); + } + selected[name] = true; + }, + + /** + * @param {string} name + */ + unSelect: function (name) { + if (this.get('selectedMode') !== 'single') { + this.option.selected[name] = false; + } + }, + + /** + * @param {string} name + */ + toggleSelected: function (name) { + var selected = this.option.selected; + // Default is true + if (!(name in selected)) { + selected[name] = true; + } + this[selected[name] ? 'unSelect' : 'select'](name); + }, + + /** + * @param {string} name + */ + isSelected: function (name) { + var selected = this.option.selected; + return !((name in selected) && !selected[name]) + && zrUtil.indexOf(this._availableNames, name) >= 0; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 4, + show: true, + + // 布局方式,默认为水平布局,可选为: + // 'horizontal' | 'vertical' + orient: 'horizontal', + + left: 'center', + // right: 'center', + + top: 'top', + // bottom: 'top', + + // 水平对齐 + // 'auto' | 'left' | 'right' + // 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐 + align: 'auto', + + backgroundColor: 'rgba(0,0,0,0)', + // 图例边框颜色 + borderColor: '#ccc', + // 图例边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + // 图例内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + // 各个item之间的间隔,单位px,默认为10, + // 横向布局时为水平间隔,纵向布局时为纵向间隔 + itemGap: 10, + // 图例图形宽度 + itemWidth: 25, + // 图例图形高度 + itemHeight: 14, + + // 图例关闭时候的颜色 + inactiveColor: '#ccc', + + textStyle: { + // 图例文字颜色 + color: '#333' + }, + // formatter: '', + // 选择模式,默认开启图例开关 + selectedMode: true, + // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入 + // selected: null, + // 图例内容(详见legend.data,数组中每一项代表一个item + // data: [], + + // Tooltip 相关配置 + tooltip: { + show: false + } + } + }); + + module.exports = LegendModel; + + +/***/ }, +/* 275 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Legend action + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + function legendSelectActionHandler(methodName, payload, ecModel) { + var selectedMap = {}; + var isToggleSelect = methodName === 'toggleSelected'; + var isSelected; + // Update all legend components + ecModel.eachComponent('legend', function (legendModel) { + if (isToggleSelect && isSelected != null) { + // Force other legend has same selected status + // Or the first is toggled to true and other are toggled to false + // In the case one legend has some item unSelected in option. And if other legend + // doesn't has the item, they will assume it is selected. + legendModel[isSelected ? 'select' : 'unSelect'](payload.name); + } + else { + legendModel[methodName](payload.name); + isSelected = legendModel.isSelected(payload.name); + } + var legendData = legendModel.getData(); + zrUtil.each(legendData, function (model) { + var name = model.get('name'); + // Wrap element + if (name === '\n' || name === '') { + return; + } + var isItemSelected = legendModel.isSelected(name); + if (name in selectedMap) { + // Unselected if any legend is unselected + selectedMap[name] = selectedMap[name] && isItemSelected; + } + else { + selectedMap[name] = isItemSelected; + } + }); + }); + // Return the event explicitly + return { + name: payload.name, + selected: selectedMap + }; + } + /** + * @event legendToggleSelect + * @type {Object} + * @property {string} type 'legendToggleSelect' + * @property {string} [from] + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendToggleSelect', 'legendselectchanged', + zrUtil.curry(legendSelectActionHandler, 'toggleSelected') + ); + + /** + * @event legendSelect + * @type {Object} + * @property {string} type 'legendSelect' + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendSelect', 'legendselected', + zrUtil.curry(legendSelectActionHandler, 'select') + ); + + /** + * @event legendUnSelect + * @type {Object} + * @property {string} type 'legendUnSelect' + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendUnSelect', 'legendunselected', + zrUtil.curry(legendSelectActionHandler, 'unSelect') + ); + + +/***/ }, +/* 276 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var symbolCreator = __webpack_require__(106); + var graphic = __webpack_require__(43); + var listComponentHelper = __webpack_require__(277); + + var curry = zrUtil.curry; + + function dispatchSelectAction(name, api) { + api.dispatchAction({ + type: 'legendToggleSelect', + name: name + }); + } + + function dispatchHighlightAction(seriesModel, dataName, api) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + seriesModel.get('legendHoverLink') && api.dispatchAction({ + type: 'highlight', + seriesName: seriesModel.name, + name: dataName + }); + } + } + + function dispatchDownplayAction(seriesModel, dataName, api) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + seriesModel.get('legendHoverLink') && api.dispatchAction({ + type: 'downplay', + seriesName: seriesModel.name, + name: dataName + }); + } + } + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'legend', + + init: function () { + this._symbolTypeStore = {}; + }, + + render: function (legendModel, ecModel, api) { + var group = this.group; + group.removeAll(); + + if (!legendModel.get('show')) { + return; + } + + var selectMode = legendModel.get('selectedMode'); + var itemAlign = legendModel.get('align'); + + if (itemAlign === 'auto') { + itemAlign = (legendModel.get('left') === 'right' + && legendModel.get('orient') === 'vertical') + ? 'right' : 'left'; + } + + var legendDrawedMap = {}; + + zrUtil.each(legendModel.getData(), function (itemModel) { + var name = itemModel.get('name'); + + // Use empty string or \n as a newline string + if (name === '' || name === '\n') { + group.add(new graphic.Group({ + newline: true + })); + return; + } + + var seriesModel = ecModel.getSeriesByName(name)[0]; + + if (legendDrawedMap[name]) { + // Have been drawed + return; + } + + // Series legend + if (seriesModel) { + var data = seriesModel.getData(); + var color = data.getVisual('color'); + + // If color is a callback function + if (typeof color === 'function') { + // Use the first data + color = color(seriesModel.getDataParams(0)); + } + + // Using rect symbol defaultly + var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect'; + var symbolType = data.getVisual('symbol'); + + var itemGroup = this._createItem( + name, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, + selectMode + ); + + itemGroup.on('click', curry(dispatchSelectAction, name, api)) + .on('mouseover', curry(dispatchHighlightAction, seriesModel, '', api)) + .on('mouseout', curry(dispatchDownplayAction, seriesModel, '', api)); + + legendDrawedMap[name] = true; + } + else { + // Data legend of pie, funnel + ecModel.eachRawSeries(function (seriesModel) { + // In case multiple series has same data name + if (legendDrawedMap[name]) { + return; + } + if (seriesModel.legendDataProvider) { + var data = seriesModel.legendDataProvider(); + var idx = data.indexOfName(name); + if (idx < 0) { + return; + } + + var color = data.getItemVisual(idx, 'color'); + + var legendSymbolType = 'roundRect'; + + var itemGroup = this._createItem( + name, itemModel, legendModel, + legendSymbolType, null, + itemAlign, color, + selectMode + ); + + itemGroup.on('click', curry(dispatchSelectAction, name, api)) + // FIXME Should not specify the series name + .on('mouseover', curry(dispatchHighlightAction, seriesModel, name, api)) + .on('mouseout', curry(dispatchDownplayAction, seriesModel, name, api)); + + legendDrawedMap[name] = true; + } + }, this); + } + + if (true) { + if (!legendDrawedMap[name]) { + console.warn(name + ' series not exists. Legend data should be same with series name or data name.'); + } + } + }, this); + + listComponentHelper.layout(group, legendModel, api); + // Render background after group is layout + // FIXME + listComponentHelper.addBackground(group, legendModel); + }, + + _createItem: function ( + name, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, selectMode + ) { + var itemWidth = legendModel.get('itemWidth'); + var itemHeight = legendModel.get('itemHeight'); + var inactiveColor = legendModel.get('inactiveColor'); + + var isSelected = legendModel.isSelected(name); + var itemGroup = new graphic.Group(); + + var textStyleModel = itemModel.getModel('textStyle'); + + var itemIcon = itemModel.get('icon'); + + var tooltipModel = itemModel.getModel('tooltip'); + + // Use user given icon first + legendSymbolType = itemIcon || legendSymbolType; + itemGroup.add(symbolCreator.createSymbol( + legendSymbolType, 0, 0, itemWidth, itemHeight, isSelected ? color : inactiveColor + )); + + // Compose symbols + // PENDING + if (!itemIcon && symbolType + // At least show one symbol, can't be all none + && ((symbolType !== legendSymbolType) || symbolType == 'none') + ) { + var size = itemHeight * 0.8; + if (symbolType === 'none') { + symbolType = 'circle'; + } + // Put symbol in the center + itemGroup.add(symbolCreator.createSymbol( + symbolType, (itemWidth - size) / 2, (itemHeight - size) / 2, size, size, + isSelected ? color : inactiveColor + )); + } + + // Text + var textX = itemAlign === 'left' ? itemWidth + 5 : -5; + var textAlign = itemAlign; + + var formatter = legendModel.get('formatter'); + var content = name; + if (typeof formatter === 'string' && formatter) { + content = formatter.replace('{name}', name); + } + else if (typeof formatter === 'function') { + content = formatter(name); + } + + var text = new graphic.Text({ + style: { + text: content, + x: textX, + y: itemHeight / 2, + fill: isSelected ? textStyleModel.getTextColor() : inactiveColor, + textFont: textStyleModel.getFont(), + textAlign: textAlign, + textVerticalAlign: 'middle' + } + }); + itemGroup.add(text); + + // Add a invisible rect to increase the area of mouse hover + var hitRect = new graphic.Rect({ + shape: itemGroup.getBoundingRect(), + invisible: true, + tooltip: tooltipModel.get('show') ? zrUtil.extend({ + content: name, + // Defaul formatter + formatter: function () { + return name; + }, + formatterParams: { + componentType: 'legend', + legendIndex: legendModel.componentIndex, + name: name, + $vars: ['name'] + } + }, tooltipModel.option) : null + }); + itemGroup.add(hitRect); + + itemGroup.eachChild(function (child) { + child.silent = true; + }); + + hitRect.silent = !selectMode; + + + + this.group.add(itemGroup); + + graphic.setHoverStyle(itemGroup); + + return itemGroup; + } + }); + + +/***/ }, +/* 277 */ +/***/ function(module, exports, __webpack_require__) { + + + // List layout + var layout = __webpack_require__(21); + var formatUtil = __webpack_require__(6); + var graphic = __webpack_require__(43); + + function positionGroup(group, model, api) { + layout.positionGroup( + group, model.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + }, + model.get('padding') + ); + } + + module.exports = { + /** + * Layout list like component. + * It will box layout each items in group of component and then position the whole group in the viewport + * @param {module:zrender/group/Group} group + * @param {module:echarts/model/Component} componentModel + * @param {module:echarts/ExtensionAPI} + */ + layout: function (group, componentModel, api) { + var rect = layout.getLayoutRect(componentModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }, componentModel.get('padding')); + layout.box( + componentModel.get('orient'), + group, + componentModel.get('itemGap'), + rect.width, + rect.height + ); + + positionGroup(group, componentModel, api); + }, + + addBackground: function (group, componentModel) { + var padding = formatUtil.normalizeCssArray( + componentModel.get('padding') + ); + var boundingRect = group.getBoundingRect(); + var style = componentModel.getItemStyle(['color', 'opacity']); + style.fill = componentModel.get('backgroundColor'); + var rect = new graphic.Rect({ + shape: { + x: boundingRect.x - padding[3], + y: boundingRect.y - padding[0], + width: boundingRect.width + padding[1] + padding[3], + height: boundingRect.height + padding[0] + padding[2] + }, + style: style, + silent: true, + z2: -1 + }); + graphic.subPixelOptimizeRect(rect); + + group.add(rect); + } + }; + + +/***/ }, +/* 278 */ +/***/ function(module, exports) { + + + module.exports = function (ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (legendModels && legendModels.length) { + ecModel.filterSeries(function (series) { + // If in any legend component the status is not selected. + // Because in legend series is assumed selected when it is not in the legend data. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(series.name)) { + return false; + } + } + return true; + }); + } + }; + + +/***/ }, +/* 279 */ +/***/ function(module, exports, __webpack_require__) { + + // FIXME Better way to pack data in graphic element + + + __webpack_require__(280); + + __webpack_require__(281); + + // Show tip action + /** + * @action + * @property {string} type + * @property {number} seriesIndex + * @property {number} dataIndex + * @property {number} [x] + * @property {number} [y] + */ + __webpack_require__(1).registerAction( + { + type: 'showTip', + event: 'showTip', + update: 'none' + }, + // noop + function () {} + ); + // Hide tip action + __webpack_require__(1).registerAction( + { + type: 'hideTip', + event: 'hideTip', + update: 'none' + }, + // noop + function () {} + ); + + +/***/ }, +/* 280 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(1).extendComponentModel({ + + type: 'tooltip', + + defaultOption: { + zlevel: 0, + + z: 8, + + show: true, + + // tooltip主体内容 + showContent: true, + + // 触发类型,默认数据触发,见下图,可选为:'item' ¦ 'axis' + trigger: 'item', + + // 触发条件,支持 'click' | 'mousemove' + triggerOn: 'mousemove', + + // 是否永远显示 content + alwaysShowContent: false, + + // 位置 {Array} | {Function} + // position: null + + // 内容格式器:{string}(Template) ¦ {Function} + // formatter: null + + showDelay: 0, + + // 隐藏延迟,单位ms + hideDelay: 100, + + // 动画变换时间,单位s + transitionDuration: 0.4, + + enterable: false, + + // 提示背景颜色,默认为透明度为0.7的黑色 + backgroundColor: 'rgba(50,50,50,0.7)', + + // 提示边框颜色 + borderColor: '#333', + + // 提示边框圆角,单位px,默认为4 + borderRadius: 4, + + // 提示边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + + // 提示内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + + // Extra css text + extraCssText: '', + + // 坐标轴指示器,坐标轴触发有效 + axisPointer: { + // 默认为直线 + // 可选为:'line' | 'shadow' | 'cross' + type: 'line', + + // type 为 line 的时候有效,指定 tooltip line 所在的轴,可选 + // 可选 'x' | 'y' | 'angle' | 'radius' | 'auto' + // 默认 'auto',会选择类型为 cateogry 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴 + // 极坐标系会默认选择 angle 轴 + axis: 'auto', + + animation: true, + animationDurationUpdate: 200, + animationEasingUpdate: 'exponentialOut', + + // 直线指示器样式设置 + lineStyle: { + color: '#555', + width: 1, + type: 'solid' + }, + + crossStyle: { + color: '#555', + width: 1, + type: 'dashed', + + // TODO formatter + textStyle: {} + }, + + // 阴影指示器样式设置 + shadowStyle: { + color: 'rgba(150,150,150,0.3)' + } + }, + textStyle: { + color: '#fff', + fontSize: 14 + } + } + }); + + +/***/ }, +/* 281 */ +/***/ function(module, exports, __webpack_require__) { + + + + var TooltipContent = __webpack_require__(282); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var env = __webpack_require__(2); + var Model = __webpack_require__(12); + + function dataEqual(a, b) { + if (!a || !b) { + return false; + } + var round = numberUtil.round; + return round(a[0]) === round(b[0]) + && round(a[1]) === round(b[1]); + } + /** + * @inner + */ + function makeLineShape(x1, y1, x2, y2) { + return { + x1: x1, + y1: y1, + x2: x2, + y2: y2 + }; + } + + /** + * @inner + */ + function makeRectShape(x, y, width, height) { + return { + x: x, + y: y, + width: width, + height: height + }; + } + + /** + * @inner + */ + function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) { + return { + cx: cx, + cy: cy, + r0: r0, + r: r, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true + }; + } + + function refixTooltipPosition(x, y, el, viewWidth, viewHeight) { + var width = el.clientWidth; + var height = el.clientHeight; + var gap = 20; + + if (x + width + gap > viewWidth) { + x -= width + gap; + } + else { + x += gap; + } + if (y + height + gap > viewHeight) { + y -= height + gap; + } + else { + y += gap; + } + return [x, y]; + } + + function calcTooltipPosition(position, rect, dom) { + var domWidth = dom.clientWidth; + var domHeight = dom.clientHeight; + var gap = 5; + var x = 0; + var y = 0; + var rectWidth = rect.width; + var rectHeight = rect.height; + switch (position) { + case 'inside': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'top': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y - domHeight - gap; + break; + case 'bottom': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight + gap; + break; + case 'left': + x = rect.x - domWidth - gap; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'right': + x = rect.x + rectWidth + gap; + y = rect.y + rectHeight / 2 - domHeight / 2; + } + return [x, y]; + } + + /** + * @param {string|Function|Array.} positionExpr + * @param {number} x Mouse x + * @param {number} y Mouse y + * @param {module:echarts/component/tooltip/TooltipContent} content + * @param {Object|} params + * @param {module:zrender/Element} el target element + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ + function updatePosition(positionExpr, x, y, content, params, el, api) { + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + + var rect = el && el.getBoundingRect().clone(); + el && rect.applyTransform(el.transform); + if (typeof positionExpr === 'function') { + // Callback of position can be an array or a string specify the position + positionExpr = positionExpr([x, y], params, content.el, rect); + } + + if (zrUtil.isArray(positionExpr)) { + x = parsePercent(positionExpr[0], viewWidth); + y = parsePercent(positionExpr[1], viewHeight); + } + // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element + else if (typeof positionExpr === 'string' && el) { + var pos = calcTooltipPosition( + positionExpr, rect, content.el + ); + x = pos[0]; + y = pos[1]; + } + else { + var pos = refixTooltipPosition( + x, y, content.el, viewWidth, viewHeight + ); + x = pos[0]; + y = pos[1]; + } + + content.moveTo(x, y); + } + + function ifSeriesSupportAxisTrigger(seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var trigger = seriesModel.get('tooltip.trigger', true); + // Ignore series use item tooltip trigger and series coordinate system is not cartesian or + return !(!coordSys + || (coordSys.type !== 'cartesian2d' && coordSys.type !== 'polar' && coordSys.type !== 'singleAxis') + || trigger === 'item'); + } + + __webpack_require__(1).extendComponentView({ + + type: 'tooltip', + + _axisPointers: {}, + + init: function (ecModel, api) { + if (env.node) { + return; + } + var tooltipContent = new TooltipContent(api.getDom(), api); + this._tooltipContent = tooltipContent; + + api.on('showTip', this._manuallyShowTip, this); + api.on('hideTip', this._manuallyHideTip, this); + }, + + render: function (tooltipModel, ecModel, api) { + if (env.node) { + return; + } + + // Reset + this.group.removeAll(); + + /** + * @type {Object} + * @private + */ + this._axisPointers = {}; + + /** + * @private + * @type {module:echarts/component/tooltip/TooltipModel} + */ + this._tooltipModel = tooltipModel; + + /** + * @private + * @type {module:echarts/model/Global} + */ + this._ecModel = ecModel; + + /** + * @private + * @type {module:echarts/ExtensionAPI} + */ + this._api = api; + + /** + * @type {Object} + * @private + */ + this._lastHover = { + // data + // payloadBatch + }; + + var tooltipContent = this._tooltipContent; + tooltipContent.update(); + tooltipContent.enterable = tooltipModel.get('enterable'); + this._alwaysShowContent = tooltipModel.get('alwaysShowContent'); + + /** + * @type {Object.} + */ + this._seriesGroupByAxis = this._prepareAxisTriggerData( + tooltipModel, ecModel + ); + + var crossText = this._crossText; + if (crossText) { + this.group.add(crossText); + } + + // Try to keep the tooltip show when refreshing + if (this._lastX != null && this._lastY != null) { + var self = this; + clearTimeout(this._refreshUpdateTimeout); + this._refreshUpdateTimeout = setTimeout(function () { + // Show tip next tick after other charts are rendered + // In case highlight action has wrong result + // FIXME + self._manuallyShowTip({ + x: self._lastX, + y: self._lastY + }); + }); + } + + var zr = this._api.getZr(); + zr.off('click', this._tryShow); + zr.off('mousemove', this._mousemove); + zr.off('mouseout', this._hide); + zr.off('globalout', this._hide); + if (tooltipModel.get('triggerOn') === 'click') { + zr.on('click', this._tryShow, this); + } + else { + zr.on('mousemove', this._mousemove, this); + zr.on('mouseout', this._hide, this); + zr.on('globalout', this._hide, this); + } + }, + + _mousemove: function (e) { + var showDelay = this._tooltipModel.get('showDelay'); + var self = this; + clearTimeout(this._showTimeout); + if (showDelay > 0) { + this._showTimeout = setTimeout(function () { + self._tryShow(e); + }, showDelay); + } + else { + this._tryShow(e); + } + }, + + /** + * Show tip manually by + * dispatchAction({ + * type: 'showTip', + * x: 10, + * y: 10 + * }); + * Or + * dispatchAction({ + * type: 'showTip', + * seriesIndex: 0, + * dataIndex: 1 + * }); + * + * TODO Batch + */ + _manuallyShowTip: function (event) { + // From self + if (event.from === this.uid) { + return; + } + + var ecModel = this._ecModel; + var seriesIndex = event.seriesIndex; + var dataIndex = event.dataIndex; + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + var api = this._api; + + if (event.x == null || event.y == null) { + if (!seriesModel) { + // Find the first series can use axis trigger + ecModel.eachSeries(function (_series) { + if (ifSeriesSupportAxisTrigger(_series) && !seriesModel) { + seriesModel = _series; + } + }); + } + if (seriesModel) { + var data = seriesModel.getData(); + if (dataIndex == null) { + dataIndex = data.indexOfName(event.name); + } + var el = data.getItemGraphicEl(dataIndex); + var cx, cy; + // Try to get the point in coordinate system + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.dataToPoint) { + var point = coordSys.dataToPoint( + data.getValues( + zrUtil.map(coordSys.dimensions, function (dim) { + return seriesModel.coordDimToDataDim(dim)[0]; + }), dataIndex, true + ) + ); + cx = point && point[0]; + cy = point && point[1]; + } + else if (el) { + // Use graphic bounding rect + var rect = el.getBoundingRect().clone(); + rect.applyTransform(el.transform); + cx = rect.x + rect.width / 2; + cy = rect.y + rect.height / 2; + } + if (cx != null && cy != null) { + this._tryShow({ + offsetX: cx, + offsetY: cy, + target: el, + event: {} + }); + } + } + } + else { + var el = api.getZr().handler.findHover(event.x, event.y); + this._tryShow({ + offsetX: event.x, + offsetY: event.y, + target: el, + event: {} + }); + } + }, + + _manuallyHideTip: function (e) { + if (e.from === this.uid) { + return; + } + + this._hide(); + }, + + _prepareAxisTriggerData: function (tooltipModel, ecModel) { + // Prepare data for axis trigger + var seriesGroupByAxis = {}; + ecModel.eachSeries(function (seriesModel) { + if (ifSeriesSupportAxisTrigger(seriesModel)) { + var coordSys = seriesModel.coordinateSystem; + var baseAxis; + var key; + + // Only cartesian2d, polar and single support axis trigger + if (coordSys.type === 'cartesian2d') { + // FIXME `axisPointer.axis` is not baseAxis + baseAxis = coordSys.getBaseAxis(); + key = baseAxis.dim + baseAxis.index; + } + else if (coordSys.type === 'singleAxis') { + baseAxis = coordSys.getAxis(); + key = baseAxis.dim + baseAxis.type; + } + else { + baseAxis = coordSys.getBaseAxis(); + key = baseAxis.dim + coordSys.name; + } + + seriesGroupByAxis[key] = seriesGroupByAxis[key] || { + coordSys: [], + series: [] + }; + seriesGroupByAxis[key].coordSys.push(coordSys); + seriesGroupByAxis[key].series.push(seriesModel); + } + }, this); + + return seriesGroupByAxis; + }, + + /** + * mousemove handler + * @param {Object} e + * @private + */ + _tryShow: function (e) { + var el = e.target; + var tooltipModel = this._tooltipModel; + var globalTrigger = tooltipModel.get('trigger'); + var ecModel = this._ecModel; + var api = this._api; + + if (!tooltipModel) { + return; + } + + // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed + this._lastX = e.offsetX; + this._lastY = e.offsetY; + + // Always show item tooltip if mouse is on the element with dataIndex + if (el && el.dataIndex != null) { + // Use dataModel in element if possible + // Used when mouseover on a element like markPoint or edge + // In which case, the data is not main data in series. + var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); + var dataIndex = el.dataIndex; + var itemModel = dataModel.getData().getItemModel(dataIndex); + // Series or single data may use item trigger when global is axis trigger + if ((itemModel.get('tooltip.trigger') || globalTrigger) === 'axis') { + this._showAxisTooltip(tooltipModel, ecModel, e); + } + else { + // Reset ticket + this._ticket = ''; + // If either single data or series use item trigger + this._hideAxisPointer(); + // Reset last hover and dispatch downplay action + this._resetLastHover(); + + this._showItemTooltipContent(dataModel, dataIndex, el.dataType, e); + } + + api.dispatchAction({ + type: 'showTip', + from: this.uid, + dataIndex: el.dataIndex, + seriesIndex: el.seriesIndex + }); + } + // Tooltip provided directly. Like legend + else if (el && el.tooltip) { + var tooltipOpt = el.tooltip; + if (typeof tooltipOpt === 'string') { + var content = tooltipOpt; + tooltipOpt = { + content: content, + // Fixed formatter + formatter: content + }; + } + var subTooltipModel = new Model(tooltipOpt, tooltipModel); + var defaultHtml = subTooltipModel.get('content'); + var asyncTicket = Math.random(); + this._showTooltipContent( + // TODO params + subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {}, + asyncTicket, e.offsetX, e.offsetY, el, api + ); + } + else { + if (globalTrigger === 'item') { + this._hide(); + } + else { + // Try show axis tooltip + this._showAxisTooltip(tooltipModel, ecModel, e); + } + + // Action of cross pointer + // other pointer types will trigger action in _dispatchAndShowSeriesTooltipContent method + if (tooltipModel.get('axisPointer.type') === 'cross') { + api.dispatchAction({ + type: 'showTip', + from: this.uid, + x: e.offsetX, + y: e.offsetY + }); + } + } + }, + + /** + * Show tooltip on axis + * @param {module:echarts/component/tooltip/TooltipModel} tooltipModel + * @param {module:echarts/model/Global} ecModel + * @param {Object} e + * @private + */ + _showAxisTooltip: function (tooltipModel, ecModel, e) { + var axisPointerModel = tooltipModel.getModel('axisPointer'); + var axisPointerType = axisPointerModel.get('type'); + + if (axisPointerType === 'cross') { + var el = e.target; + if (el && el.dataIndex != null) { + var seriesModel = ecModel.getSeriesByIndex(el.seriesIndex); + var dataIndex = el.dataIndex; + this._showItemTooltipContent(seriesModel, dataIndex, el.dataType, e); + } + } + + this._showAxisPointer(); + var allNotShow = true; + zrUtil.each(this._seriesGroupByAxis, function (seriesCoordSysSameAxis) { + // Try show the axis pointer + var allCoordSys = seriesCoordSysSameAxis.coordSys; + var coordSys = allCoordSys[0]; + + // If mouse position is not in the grid or polar + var point = [e.offsetX, e.offsetY]; + + if (!coordSys.containPoint(point)) { + // Hide axis pointer + this._hideAxisPointer(coordSys.name); + return; + } + + allNotShow = false; + // Make sure point is discrete on cateogry axis + var dimensions = coordSys.dimensions; + var value = coordSys.pointToData(point, true); + point = coordSys.dataToPoint(value); + var baseAxis = coordSys.getBaseAxis(); + var axisType = axisPointerModel.get('axis'); + if (axisType === 'auto') { + axisType = baseAxis.dim; + } + + var contentNotChange = false; + var lastHover = this._lastHover; + if (axisPointerType === 'cross') { + // If hover data not changed + // Possible when two axes are all category + if (dataEqual(lastHover.data, value)) { + contentNotChange = true; + } + lastHover.data = value; + } + else { + var valIndex = zrUtil.indexOf(dimensions, axisType); + + // If hover data not changed on the axis dimension + if (lastHover.data === value[valIndex]) { + contentNotChange = true; + } + lastHover.data = value[valIndex]; + } + + if (coordSys.type === 'cartesian2d' && !contentNotChange) { + this._showCartesianPointer( + axisPointerModel, coordSys, axisType, point + ); + } + else if (coordSys.type === 'polar' && !contentNotChange) { + this._showPolarPointer( + axisPointerModel, coordSys, axisType, point + ); + } + else if (coordSys.type === 'singleAxis' && !contentNotChange) { + this._showSinglePointer( + axisPointerModel, coordSys, axisType, point + ); + } + + if (axisPointerType !== 'cross') { + this._dispatchAndShowSeriesTooltipContent( + coordSys, seriesCoordSysSameAxis.series, point, value, contentNotChange + ); + } + }, this); + + if (!this._tooltipModel.get('show')) { + this._hideAxisPointer(); + } + + if (allNotShow) { + this._hide(); + } + }, + + /** + * Show tooltip on axis of cartesian coordinate + * @param {module:echarts/model/Model} axisPointerModel + * @param {module:echarts/coord/cartesian/Cartesian2D} cartesians + * @param {string} axisType + * @param {Array.} point + * @private + */ + _showCartesianPointer: function (axisPointerModel, cartesian, axisType, point) { + var self = this; + + var axisPointerType = axisPointerModel.get('type'); + var baseAxis = cartesian.getBaseAxis(); + var moveAnimation = axisPointerType !== 'cross' + && baseAxis.type === 'category' + && baseAxis.getBandWidth() > 20; + + if (axisPointerType === 'cross') { + moveGridLine('x', point, cartesian.getAxis('y').getGlobalExtent()); + moveGridLine('y', point, cartesian.getAxis('x').getGlobalExtent()); + + this._updateCrossText(cartesian, point, axisPointerModel); + } + else { + var otherAxis = cartesian.getAxis(axisType === 'x' ? 'y' : 'x'); + var otherExtent = otherAxis.getGlobalExtent(); + + if (cartesian.type === 'cartesian2d') { + (axisPointerType === 'line' ? moveGridLine : moveGridShadow)( + axisType, point, otherExtent + ); + } + } + + /** + * @inner + */ + function moveGridLine(axisType, point, otherExtent) { + var targetShape = axisType === 'x' + ? makeLineShape(point[0], otherExtent[0], point[0], otherExtent[1]) + : makeLineShape(otherExtent[0], point[1], otherExtent[1], point[1]); + + var pointerEl = self._getPointerElement( + cartesian, axisPointerModel, axisType, targetShape + ); + graphic.subPixelOptimizeLine({ + shape: targetShape, + style: pointerEl.style + }); + + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + /** + * @inner + */ + function moveGridShadow(axisType, point, otherExtent) { + var axis = cartesian.getAxis(axisType); + var bandWidth = axis.getBandWidth(); + var span = otherExtent[1] - otherExtent[0]; + var targetShape = axisType === 'x' + ? makeRectShape(point[0] - bandWidth / 2, otherExtent[0], bandWidth, span) + : makeRectShape(otherExtent[0], point[1] - bandWidth / 2, span, bandWidth); + + var pointerEl = self._getPointerElement( + cartesian, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + }, + + _showSinglePointer: function (axisPointerModel, single, axisType, point) { + var self = this; + var axisPointerType = axisPointerModel.get('type'); + var moveAnimation = axisPointerType !== 'cross' && single.getBaseAxis().type === 'category'; + var rect = single.getRect(); + var otherExtent = [rect.y, rect.y + rect.height]; + + moveSingleLine(axisType, point, otherExtent); + + /** + * @inner + */ + function moveSingleLine(axisType, point, otherExtent) { + var axis = single.getAxis(); + var orient = axis.orient; + + var targetShape = orient === 'horizontal' + ? makeLineShape(point[0], otherExtent[0], point[0], otherExtent[1]) + : makeLineShape(otherExtent[0], point[1], otherExtent[1], point[1]); + + var pointerEl = self._getPointerElement( + single, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + }, + + /** + * Show tooltip on axis of polar coordinate + * @param {module:echarts/model/Model} axisPointerModel + * @param {Array.} polar + * @param {string} axisType + * @param {Array.} point + */ + _showPolarPointer: function (axisPointerModel, polar, axisType, point) { + var self = this; + + var axisPointerType = axisPointerModel.get('type'); + + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + + var moveAnimation = axisPointerType !== 'cross' + && polar.getBaseAxis().type === 'category'; + + if (axisPointerType === 'cross') { + movePolarLine('angle', point, radiusAxis.getExtent()); + movePolarLine('radius', point, angleAxis.getExtent()); + + this._updateCrossText(polar, point, axisPointerModel); + } + else { + var otherAxis = polar.getAxis(axisType === 'radius' ? 'angle' : 'radius'); + var otherExtent = otherAxis.getExtent(); + + (axisPointerType === 'line' ? movePolarLine : movePolarShadow)( + axisType, point, otherExtent + ); + } + /** + * @inner + */ + function movePolarLine(axisType, point, otherExtent) { + var mouseCoord = polar.pointToCoord(point); + + var targetShape; + + if (axisType === 'angle') { + var p1 = polar.coordToPoint([otherExtent[0], mouseCoord[1]]); + var p2 = polar.coordToPoint([otherExtent[1], mouseCoord[1]]); + targetShape = makeLineShape(p1[0], p1[1], p2[0], p2[1]); + } + else { + targetShape = { + cx: polar.cx, + cy: polar.cy, + r: mouseCoord[0] + }; + } + + var pointerEl = self._getPointerElement( + polar, axisPointerModel, axisType, targetShape + ); + + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + /** + * @inner + */ + function movePolarShadow(axisType, point, otherExtent) { + var axis = polar.getAxis(axisType); + var bandWidth = axis.getBandWidth(); + + var mouseCoord = polar.pointToCoord(point); + + var targetShape; + + var radian = Math.PI / 180; + + if (axisType === 'angle') { + targetShape = makeSectorShape( + polar.cx, polar.cy, + otherExtent[0], otherExtent[1], + // In ECharts y is negative if angle is positive + (-mouseCoord[1] - bandWidth / 2) * radian, + (-mouseCoord[1] + bandWidth / 2) * radian + ); + } + else { + targetShape = makeSectorShape( + polar.cx, polar.cy, + mouseCoord[0] - bandWidth / 2, + mouseCoord[0] + bandWidth / 2, + 0, Math.PI * 2 + ); + } + + var pointerEl = self._getPointerElement( + polar, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + }, + + _updateCrossText: function (coordSys, point, axisPointerModel) { + var crossStyleModel = axisPointerModel.getModel('crossStyle'); + var textStyleModel = crossStyleModel.getModel('textStyle'); + + var tooltipModel = this._tooltipModel; + + var text = this._crossText; + if (!text) { + text = this._crossText = new graphic.Text({ + style: { + textAlign: 'left', + textVerticalAlign: 'bottom' + } + }); + this.group.add(text); + } + + var value = coordSys.pointToData(point); + + var dims = coordSys.dimensions; + value = zrUtil.map(value, function (val, idx) { + var axis = coordSys.getAxis(dims[idx]); + if (axis.type === 'category' || axis.type === 'time') { + val = axis.scale.getLabel(val); + } + else { + val = formatUtil.addCommas( + val.toFixed(axis.getPixelPrecision()) + ); + } + return val; + }); + + text.setStyle({ + fill: textStyleModel.getTextColor() || crossStyleModel.get('color'), + textFont: textStyleModel.getFont(), + text: value.join(', '), + x: point[0] + 5, + y: point[1] - 5 + }); + text.z = tooltipModel.get('z'); + text.zlevel = tooltipModel.get('zlevel'); + }, + + _getPointerElement: function (coordSys, pointerModel, axisType, initShape) { + var tooltipModel = this._tooltipModel; + var z = tooltipModel.get('z'); + var zlevel = tooltipModel.get('zlevel'); + var axisPointers = this._axisPointers; + var coordSysName = coordSys.name; + axisPointers[coordSysName] = axisPointers[coordSysName] || {}; + if (axisPointers[coordSysName][axisType]) { + return axisPointers[coordSysName][axisType]; + } + + // Create if not exists + var pointerType = pointerModel.get('type'); + var styleModel = pointerModel.getModel(pointerType + 'Style'); + var isShadow = pointerType === 'shadow'; + var style = styleModel[isShadow ? 'getAreaStyle' : 'getLineStyle'](); + + var elementType = coordSys.type === 'polar' + ? (isShadow ? 'Sector' : (axisType === 'radius' ? 'Circle' : 'Line')) + : (isShadow ? 'Rect' : 'Line'); + + isShadow ? (style.stroke = null) : (style.fill = null); + + var el = axisPointers[coordSysName][axisType] = new graphic[elementType]({ + style: style, + z: z, + zlevel: zlevel, + silent: true, + shape: initShape + }); + + this.group.add(el); + return el; + }, + + /** + * Dispatch actions and show tooltip on series + * @param {Array.} seriesList + * @param {Array.} point + * @param {Array.} value + * @param {boolean} contentNotChange + * @param {Object} e + */ + _dispatchAndShowSeriesTooltipContent: function ( + coordSys, seriesList, point, value, contentNotChange + ) { + + var rootTooltipModel = this._tooltipModel; + + var baseAxis = coordSys.getBaseAxis(); + var baseDimIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; + + var payloadBatch = zrUtil.map(seriesList, function (series) { + return { + seriesIndex: series.seriesIndex, + dataIndex: series.getAxisTooltipDataIndex + ? series.getAxisTooltipDataIndex(series.coordDimToDataDim(baseAxis.dim), value, baseAxis) + : series.getData().indexOfNearest( + series.coordDimToDataDim(baseAxis.dim)[0], + value[baseDimIndex], + // Add a threshold to avoid find the wrong dataIndex when data length is not same + false, baseAxis.type === 'category' ? 0.5 : null + ) + }; + }); + + var lastHover = this._lastHover; + var api = this._api; + // Dispatch downplay action + if (lastHover.payloadBatch && !contentNotChange) { + api.dispatchAction({ + type: 'downplay', + batch: lastHover.payloadBatch + }); + } + // Dispatch highlight action + if (!contentNotChange) { + api.dispatchAction({ + type: 'highlight', + batch: payloadBatch + }); + lastHover.payloadBatch = payloadBatch; + } + // Dispatch showTip action + api.dispatchAction({ + type: 'showTip', + dataIndex: payloadBatch[0].dataIndex, + seriesIndex: payloadBatch[0].seriesIndex, + from: this.uid + }); + + if (baseAxis && rootTooltipModel.get('showContent') && rootTooltipModel.get('show')) { + var paramsList = zrUtil.map(seriesList, function (series, index) { + return series.getDataParams(payloadBatch[index].dataIndex); + }); + + if (!contentNotChange) { + // Update html content + var firstDataIndex = payloadBatch[0].dataIndex; + + // Default tooltip content + // FIXME + // (1) shold be the first data which has name? + // (2) themeRiver, firstDataIndex is array, and first line is unnecessary. + var firstLine = baseAxis.type === 'time' + ? baseAxis.scale.getLabel(value[baseDimIndex]) + : seriesList[0].getData().getName(firstDataIndex); + var defaultHtml = (firstLine ? firstLine + '
' : '') + + zrUtil.map(seriesList, function (series, index) { + return series.formatTooltip(payloadBatch[index].dataIndex, true); + }).join('
'); + + var asyncTicket = 'axis_' + coordSys.name + '_' + firstDataIndex; + + this._showTooltipContent( + rootTooltipModel, defaultHtml, paramsList, asyncTicket, + point[0], point[1], null, api + ); + } + else { + updatePosition( + rootTooltipModel.get('position'), point[0], point[1], + this._tooltipContent, paramsList, null, api + ); + } + } + }, + + /** + * Show tooltip on item + * @param {module:echarts/model/Series} seriesModel + * @param {number} dataIndex + * @param {string} dataType + * @param {Object} e + */ + _showItemTooltipContent: function (seriesModel, dataIndex, dataType, e) { + // FIXME Graph data + var api = this._api; + var data = seriesModel.getData(dataType); + var itemModel = data.getItemModel(dataIndex); + + var tooltipOpt = itemModel.get('tooltip', true); + if (typeof tooltipOpt === 'string') { + // In each data item tooltip can be simply write: + // { + // value: 10, + // tooltip: 'Something you need to know' + // } + var tooltipContent = tooltipOpt; + tooltipOpt = { + formatter: tooltipContent + }; + } + var rootTooltipModel = this._tooltipModel; + var seriesTooltipModel = seriesModel.getModel( + 'tooltip', rootTooltipModel + ); + var tooltipModel = new Model(tooltipOpt, seriesTooltipModel, seriesTooltipModel.ecModel); + + var params = seriesModel.getDataParams(dataIndex, dataType); + var defaultHtml = seriesModel.formatTooltip(dataIndex, false, dataType); + + var asyncTicket = 'item_' + seriesModel.name + '_' + dataIndex; + + this._showTooltipContent( + tooltipModel, defaultHtml, params, asyncTicket, + e.offsetX, e.offsetY, e.target, api + ); + }, + + _showTooltipContent: function ( + tooltipModel, defaultHtml, params, asyncTicket, x, y, target, api + ) { + // Reset ticket + this._ticket = ''; + + if (tooltipModel.get('showContent') && tooltipModel.get('show')) { + var tooltipContent = this._tooltipContent; + + var formatter = tooltipModel.get('formatter'); + var positionExpr = tooltipModel.get('position'); + var html = defaultHtml; + + if (formatter) { + if (typeof formatter === 'string') { + html = formatUtil.formatTpl(formatter, params); + } + else if (typeof formatter === 'function') { + var self = this; + var ticket = asyncTicket; + var callback = function (cbTicket, html) { + if (cbTicket === self._ticket) { + tooltipContent.setContent(html); + + updatePosition( + positionExpr, x, y, + tooltipContent, params, target, api + ); + } + }; + self._ticket = ticket; + html = formatter(params, ticket, callback); + } + } + + tooltipContent.show(tooltipModel); + tooltipContent.setContent(html); + + updatePosition( + positionExpr, x, y, + tooltipContent, params, target, api + ); + } + }, + + /** + * Show axis pointer + * @param {string} [coordSysName] + */ + _showAxisPointer: function (coordSysName) { + if (coordSysName) { + var axisPointers = this._axisPointers[coordSysName]; + axisPointers && zrUtil.each(axisPointers, function (el) { + el.show(); + }); + } + else { + this.group.eachChild(function (child) { + child.show(); + }); + this.group.show(); + } + }, + + _resetLastHover: function () { + var lastHover = this._lastHover; + if (lastHover.payloadBatch) { + this._api.dispatchAction({ + type: 'downplay', + batch: lastHover.payloadBatch + }); + } + // Reset lastHover + this._lastHover = {}; + }, + /** + * Hide axis pointer + * @param {string} [coordSysName] + */ + _hideAxisPointer: function (coordSysName) { + if (coordSysName) { + var axisPointers = this._axisPointers[coordSysName]; + axisPointers && zrUtil.each(axisPointers, function (el) { + el.hide(); + }); + } + else { + if (this.group.children().length) { + this.group.hide(); + } + } + }, + + _hide: function () { + clearTimeout(this._showTimeout); + + this._hideAxisPointer(); + this._resetLastHover(); + if (!this._alwaysShowContent) { + this._tooltipContent.hideLater(this._tooltipModel.get('hideDelay')); + } + + this._api.dispatchAction({ + type: 'hideTip', + from: this.uid + }); + + this._lastX = this._lastY = null; + }, + + dispose: function (ecModel, api) { + if (env.node) { + return; + } + var zr = api.getZr(); + this._tooltipContent.hide(); + + zr.off('click', this._tryShow); + zr.off('mousemove', this._mousemove); + zr.off('mouseout', this._hide); + zr.off('globalout', this._hide); + + api.off('showTip', this._manuallyShowTip); + api.off('hideTip', this._manuallyHideTip); + } + }); + + +/***/ }, +/* 282 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/tooltip/TooltipContent + */ + + + var zrUtil = __webpack_require__(4); + var zrColor = __webpack_require__(39); + var eventUtil = __webpack_require__(87); + var formatUtil = __webpack_require__(6); + var each = zrUtil.each; + var toCamelCase = formatUtil.toCamelCase; + var env = __webpack_require__(2); + + var vendors = ['', '-webkit-', '-moz-', '-o-']; + + var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;'; + + /** + * @param {number} duration + * @return {string} + * @inner + */ + function assembleTransition(duration) { + var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)'; + var transitionText = 'left ' + duration + 's ' + transitionCurve + ',' + + 'top ' + duration + 's ' + transitionCurve; + return zrUtil.map(vendors, function (vendorPrefix) { + return vendorPrefix + 'transition:' + transitionText; + }).join(';'); + } + + /** + * @param {Object} textStyle + * @return {string} + * @inner + */ + function assembleFont(textStyleModel) { + var cssText = []; + + var fontSize = textStyleModel.get('fontSize'); + var color = textStyleModel.getTextColor(); + + color && cssText.push('color:' + color); + + cssText.push('font:' + textStyleModel.getFont()); + + fontSize && + cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px'); + + each(['decoration', 'align'], function (name) { + var val = textStyleModel.get(name); + val && cssText.push('text-' + name + ':' + val); + }); + + return cssText.join(';'); + } + + /** + * @param {Object} tooltipModel + * @return {string} + * @inner + */ + function assembleCssText(tooltipModel) { + + tooltipModel = tooltipModel; + + var cssText = []; + + var transitionDuration = tooltipModel.get('transitionDuration'); + var backgroundColor = tooltipModel.get('backgroundColor'); + var textStyleModel = tooltipModel.getModel('textStyle'); + var padding = tooltipModel.get('padding'); + + // Animation transition + transitionDuration && + cssText.push(assembleTransition(transitionDuration)); + + if (backgroundColor) { + if (env.canvasSupported) { + cssText.push('background-Color:' + backgroundColor); + } + else { + // for ie + cssText.push( + 'background-Color:#' + zrColor.toHex(backgroundColor) + ); + cssText.push('filter:alpha(opacity=70)'); + } + } + + // Border style + each(['width', 'color', 'radius'], function (name) { + var borderName = 'border-' + name; + var camelCase = toCamelCase(borderName); + var val = tooltipModel.get(camelCase); + val != null && + cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px')); + }); + + // Text style + cssText.push(assembleFont(textStyleModel)); + + // Padding + if (padding != null) { + cssText.push('padding:' + formatUtil.normalizeCssArray(padding).join('px ') + 'px'); + } + + return cssText.join(';') + ';'; + } + + /** + * @alias module:echarts/component/tooltip/TooltipContent + * @constructor + */ + function TooltipContent(container, api) { + var el = document.createElement('div'); + var zr = api.getZr(); + + this.el = el; + + this._x = api.getWidth() / 2; + this._y = api.getHeight() / 2; + + container.appendChild(el); + + this._container = container; + + this._show = false; + + /** + * @private + */ + this._hideTimeout; + + var self = this; + el.onmouseenter = function () { + // clear the timeout in hideLater and keep showing tooltip + if (self.enterable) { + clearTimeout(self._hideTimeout); + self._show = true; + } + self._inContent = true; + }; + el.onmousemove = function (e) { + if (!self.enterable) { + // Try trigger zrender event to avoid mouse + // in and out shape too frequently + var handler = zr.handler; + eventUtil.normalizeEvent(container, e); + handler.dispatch('mousemove', e); + } + }; + el.onmouseleave = function () { + if (self.enterable) { + if (self._show) { + self.hideLater(self._hideDelay); + } + } + self._inContent = false; + }; + + compromiseMobile(el, container); + } + + function compromiseMobile(tooltipContentEl, container) { + // Prevent default behavior on mobile. For example, + // default pinch gesture will cause browser zoom. + // We do not preventing event on tooltip contnet el, + // because user may need customization in tooltip el. + eventUtil.addEventListener(container, 'touchstart', preventDefault); + eventUtil.addEventListener(container, 'touchmove', preventDefault); + eventUtil.addEventListener(container, 'touchend', preventDefault); + + function preventDefault(e) { + if (contains(e.target)) { + e.preventDefault(); + } + } + + function contains(targetEl) { + while (targetEl && targetEl !== container) { + if (targetEl === tooltipContentEl) { + return true; + } + targetEl = targetEl.parentNode; + } + } + } + + TooltipContent.prototype = { + + constructor: TooltipContent, + + enterable: true, + + /** + * Update when tooltip is rendered + */ + update: function () { + var container = this._container; + var stl = container.currentStyle + || document.defaultView.getComputedStyle(container); + var domStyle = container.style; + if (domStyle.position !== 'absolute' && stl.position !== 'absolute') { + domStyle.position = 'relative'; + } + // Hide the tooltip + // PENDING + // this.hide(); + }, + + show: function (tooltipModel) { + clearTimeout(this._hideTimeout); + var el = this.el; + + el.style.cssText = gCssText + assembleCssText(tooltipModel) + // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore + + ';left:' + this._x + 'px;top:' + this._y + 'px;' + + (tooltipModel.get('extraCssText') || ''); + + el.style.display = el.innerHTML ? 'block' : 'none'; + + this._show = true; + }, + + setContent: function (content) { + var el = this.el; + el.innerHTML = content; + el.style.display = content ? 'block' : 'none'; + }, + + moveTo: function (x, y) { + var style = this.el.style; + style.left = x + 'px'; + style.top = y + 'px'; + + this._x = x; + this._y = y; + }, + + hide: function () { + this.el.style.display = 'none'; + this._show = false; + }, + + // showLater: function () + + hideLater: function (time) { + if (this._show && !(this._inContent && this.enterable)) { + if (time) { + this._hideDelay = time; + // Set show false to avoid invoke hideLater mutiple times + this._show = false; + this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time); + } + else { + this.hide(); + } + } + }, + + isShow: function () { + return this._show; + } + }; + + module.exports = TooltipContent; + + +/***/ }, +/* 283 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(284); + __webpack_require__(290); + __webpack_require__(292); + + // Polar view + __webpack_require__(1).extendComponentView({ + type: 'polar' + }); + + +/***/ }, +/* 284 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Axis scale + + + var Polar = __webpack_require__(285); + var numberUtil = __webpack_require__(7); + var zrUtil = __webpack_require__(4); + + var axisHelper = __webpack_require__(114); + var niceScaleExtent = axisHelper.niceScaleExtent; + + // 依赖 PolarModel 做预处理 + __webpack_require__(288); + + /** + * Resize method bound to the polar + * @param {module:echarts/coord/polar/PolarModel} polarModel + * @param {module:echarts/ExtensionAPI} api + */ + function resizePolar(polarModel, api) { + var center = polarModel.get('center'); + var radius = polarModel.get('radius'); + var width = api.getWidth(); + var height = api.getHeight(); + var parsePercent = numberUtil.parsePercent; + + this.cx = parsePercent(center[0], width); + this.cy = parsePercent(center[1], height); + + var radiusAxis = this.getRadiusAxis(); + var size = Math.min(width, height) / 2; + // var idx = radiusAxis.inverse ? 1 : 0; + radiusAxis.setExtent(0, parsePercent(radius, size)); + } + + /** + * Update polar + */ + function updatePolarScale(ecModel, api) { + var polar = this; + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + // Reset scale + angleAxis.scale.setExtent(Infinity, -Infinity); + radiusAxis.scale.setExtent(Infinity, -Infinity); + + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.coordinateSystem === polar) { + var data = seriesModel.getData(); + radiusAxis.scale.unionExtent( + data.getDataExtent('radius', radiusAxis.type !== 'category') + ); + angleAxis.scale.unionExtent( + data.getDataExtent('angle', angleAxis.type !== 'category') + ); + } + }); + + niceScaleExtent(angleAxis, angleAxis.model); + niceScaleExtent(radiusAxis, radiusAxis.model); + + // Fix extent of category angle axis + if (angleAxis.type === 'category' && !angleAxis.onBand) { + var extent = angleAxis.getExtent(); + var diff = 360 / angleAxis.scale.count(); + angleAxis.inverse ? (extent[1] += diff) : (extent[1] -= diff); + angleAxis.setExtent(extent[0], extent[1]); + } + } + + /** + * Set common axis properties + * @param {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + * @param {module:echarts/coord/polar/AxisModel} + * @inner + */ + function setAxis(axis, axisModel) { + axis.type = axisModel.get('type'); + axis.scale = axisHelper.createScaleByModel(axisModel); + axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category'; + + // FIXME Radius axis not support inverse axis + if (axisModel.mainType === 'angleAxis') { + var startAngle = axisModel.get('startAngle'); + axis.inverse = axisModel.get('inverse') ^ axisModel.get('clockwise'); + axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360)); + } + + // Inject axis instance + axisModel.axis = axis; + axis.model = axisModel; + } + + + var polarCreator = { + + dimensions: Polar.prototype.dimensions, + + create: function (ecModel, api) { + var polarList = []; + ecModel.eachComponent('polar', function (polarModel, idx) { + var polar = new Polar(idx); + // Inject resize and update method + polar.resize = resizePolar; + polar.update = updatePolarScale; + + var radiusAxis = polar.getRadiusAxis(); + var angleAxis = polar.getAngleAxis(); + + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + + setAxis(radiusAxis, radiusAxisModel); + setAxis(angleAxis, angleAxisModel); + + polar.resize(polarModel, api); + polarList.push(polar); + + polarModel.coordinateSystem = polar; + }); + // Inject coordinateSystem to series + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'polar') { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: seriesModel.get('polarIndex'), + id: seriesModel.get('polarId') + })[0]; + + if (true) { + if (!polarModel) { + throw new Error( + 'Polar "' + zrUtil.retrieve( + seriesModel.get('polarIndex'), + seriesModel.get('polarId'), + 0 + ) + '" not found' + ); + } + } + seriesModel.coordinateSystem = polarModel.coordinateSystem; + } + }); + + return polarList; + } + }; + + __webpack_require__(26).register('polar', polarCreator); + + +/***/ }, +/* 285 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module echarts/coord/polar/Polar + */ + + + var RadiusAxis = __webpack_require__(286); + var AngleAxis = __webpack_require__(287); + + /** + * @alias {module:echarts/coord/polar/Polar} + * @constructor + * @param {string} name + */ + var Polar = function (name) { + + /** + * @type {string} + */ + this.name = name || ''; + + /** + * x of polar center + * @type {number} + */ + this.cx = 0; + + /** + * y of polar center + * @type {number} + */ + this.cy = 0; + + /** + * @type {module:echarts/coord/polar/RadiusAxis} + * @private + */ + this._radiusAxis = new RadiusAxis(); + + /** + * @type {module:echarts/coord/polar/AngleAxis} + * @private + */ + this._angleAxis = new AngleAxis(); + }; + + Polar.prototype = { + + constructor: Polar, + + type: 'polar', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['radius', 'angle'], + + /** + * If contain coord + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var coord = this.pointToCoord(point); + return this._radiusAxis.contain(coord[0]) + && this._angleAxis.contain(coord[1]); + }, + + /** + * If contain data + * @param {Array.} data + * @return {boolean} + */ + containData: function (data) { + return this._radiusAxis.containData(data[0]) + && this._angleAxis.containData(data[1]); + }, + + /** + * @param {string} axisType + * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + getAxis: function (axisType) { + return this['_' + axisType + 'Axis']; + }, + + /** + * Get axes by type of scale + * @param {string} scaleType + * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + getAxesByScale: function (scaleType) { + var axes = []; + var angleAxis = this._angleAxis; + var radiusAxis = this._radiusAxis; + angleAxis.scale.type === scaleType && axes.push(angleAxis); + radiusAxis.scale.type === scaleType && axes.push(radiusAxis); + + return axes; + }, + + /** + * @return {module:echarts/coord/polar/AngleAxis} + */ + getAngleAxis: function () { + return this._angleAxis; + }, + + /** + * @return {module:echarts/coord/polar/RadiusAxis} + */ + getRadiusAxis: function () { + return this._radiusAxis; + }, + + /** + * @param {module:echarts/coord/polar/Axis} + * @return {module:echarts/coord/polar/Axis} + */ + getOtherAxis: function (axis) { + var angleAxis = this._angleAxis; + return axis === angleAxis ? this._radiusAxis : angleAxis; + }, + + /** + * Base axis will be used on stacking. + * + * @return {module:echarts/coord/polar/Axis} + */ + getBaseAxis: function () { + return this.getAxesByScale('ordinal')[0] + || this.getAxesByScale('time')[0] + || this.getAngleAxis(); + }, + + /** + * Convert series data to a list of (x, y) points + * @param {module:echarts/data/List} data + * @return {Array} + * Return list of coordinates. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data) { + return data.mapArray(this.dimensions, function (radius, angle) { + return this.dataToPoint([radius, angle]); + }, this); + }, + + /** + * Convert a single data item to (x, y) point. + * Parameter data is an array which the first element is radius and the second is angle + * @param {Array.} data + * @param {boolean} [clamp=false] + * @return {Array.} + */ + dataToPoint: function (data, clamp) { + return this.coordToPoint([ + this._radiusAxis.dataToRadius(data[0], clamp), + this._angleAxis.dataToAngle(data[1], clamp) + ]); + }, + + /** + * Convert a (x, y) point to data + * @param {Array.} point + * @param {boolean} [clamp=false] + * @return {Array.} + */ + pointToData: function (point, clamp) { + var coord = this.pointToCoord(point); + return [ + this._radiusAxis.radiusToData(coord[0], clamp), + this._angleAxis.angleToData(coord[1], clamp) + ]; + }, + + /** + * Convert a (x, y) point to (radius, angle) coord + * @param {Array.} point + * @return {Array.} + */ + pointToCoord: function (point) { + var dx = point[0] - this.cx; + var dy = point[1] - this.cy; + var angleAxis = this.getAngleAxis(); + var extent = angleAxis.getExtent(); + var minAngle = Math.min(extent[0], extent[1]); + var maxAngle = Math.max(extent[0], extent[1]); + // Fix fixed extent in polarCreator + // FIXME + angleAxis.inverse + ? (minAngle = maxAngle - 360) + : (maxAngle = minAngle + 360); + + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + + var radian = Math.atan2(-dy, dx) / Math.PI * 180; + + // move to angleExtent + var dir = radian < minAngle ? 1 : -1; + while (radian < minAngle || radian > maxAngle) { + radian += dir * 360; + } + + return [radius, radian]; + }, + + /** + * Convert a (radius, angle) coord to (x, y) point + * @param {Array.} coord + * @return {Array.} + */ + coordToPoint: function (coord) { + var radius = coord[0]; + var radian = coord[1] / 180 * Math.PI; + var x = Math.cos(radian) * radius + this.cx; + // Inverse the y + var y = -Math.sin(radian) * radius + this.cy; + + return [x, y]; + } + }; + + module.exports = Polar; + + +/***/ }, +/* 286 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function RadiusAxis(scale, radiusExtent) { + + Axis.call(this, 'radius', scale, radiusExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'category'; + } + + RadiusAxis.prototype = { + + constructor: RadiusAxis, + + dataToRadius: Axis.prototype.dataToCoord, + + radiusToData: Axis.prototype.coordToData + }; + + zrUtil.inherits(RadiusAxis, Axis); + + module.exports = RadiusAxis; + + +/***/ }, +/* 287 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function AngleAxis(scale, angleExtent) { + + angleExtent = angleExtent || [0, 360]; + + Axis.call(this, 'angle', scale, angleExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'category'; + } + + AngleAxis.prototype = { + + constructor: AngleAxis, + + dataToAngle: Axis.prototype.dataToCoord, + + angleToData: Axis.prototype.coordToData + }; + + zrUtil.inherits(AngleAxis, Axis); + + module.exports = AngleAxis; + + +/***/ }, +/* 288 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(289); + + __webpack_require__(1).extendComponentModel({ + + type: 'polar', + + dependencies: ['polarAxis', 'angleAxis'], + + /** + * @type {module:echarts/coord/polar/Polar} + */ + coordinateSystem: null, + + /** + * @param {string} axisType + * @return {module:echarts/coord/polar/AxisModel} + */ + findAxisModel: function (axisType) { + var foundAxisModel; + var ecModel = this.ecModel; + + ecModel.eachComponent(axisType, function (axisModel) { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: axisModel.getShallow('polarIndex'), + id: axisModel.getShallow('polarId') + })[0]; + + if(polarModel === this) { + foundAxisModel = axisModel; + } + }, this); + return foundAxisModel; + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + center: ['50%', '50%'], + + radius: '80%' + } + }); + + +/***/ }, +/* 289 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ComponentModel = __webpack_require__(19); + var axisModelCreator = __webpack_require__(127); + + var PolarAxisModel = ComponentModel.extend({ + type: 'polarAxis', + /** + * @type {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + axis: null + }); + + zrUtil.merge(PolarAxisModel.prototype, __webpack_require__(129)); + + var polarAxisDefaultExtendedOption = { + angle: { + // polarIndex: 0, + // polarId: '', + + startAngle: 90, + + clockwise: true, + + splitNumber: 12, + + axisLabel: { + rotate: false + } + }, + radius: { + // polarIndex: 0, + // polarId: '', + + splitNumber: 5 + } + }; + + function getAxisType(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); + } + + axisModelCreator('angle', PolarAxisModel, getAxisType, polarAxisDefaultExtendedOption.angle); + axisModelCreator('radius', PolarAxisModel, getAxisType, polarAxisDefaultExtendedOption.radius); + + + +/***/ }, +/* 290 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(284); + + __webpack_require__(291); + + +/***/ }, +/* 291 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + + var elementList = ['axisLine', 'axisLabel', 'axisTick', 'splitLine', 'splitArea']; + + function getAxisLineShape(polar, r0, r, angle) { + var start = polar.coordToPoint([r0, angle]); + var end = polar.coordToPoint([r, angle]); + + return { + x1: start[0], + y1: start[1], + x2: end[0], + y2: end[1] + }; + } + __webpack_require__(1).extendComponentView({ + + type: 'angleAxis', + + render: function (angleAxisModel, ecModel) { + this.group.removeAll(); + if (!angleAxisModel.get('show')) { + return; + } + + var polarModel = ecModel.getComponent('polar', angleAxisModel.get('polarIndex')); + var angleAxis = angleAxisModel.axis; + var polar = polarModel.coordinateSystem; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var ticksAngles = angleAxis.getTicksCoords(); + + if (angleAxis.type !== 'category') { + // Remove the last tick which will overlap the first tick + ticksAngles.pop(); + } + + zrUtil.each(elementList, function (name) { + if (angleAxisModel.get(name +'.show')) { + this['_' + name](angleAxisModel, polar, ticksAngles, radiusExtent); + } + }, this); + }, + + /** + * @private + */ + _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle'); + + var circle = new graphic.Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: radiusExtent[1] + }, + style: lineStyleModel.getLineStyle(), + z2: 1, + silent: true + }); + circle.style.fill = null; + + this.group.add(circle); + }, + + /** + * @private + */ + _axisTick: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var tickModel = angleAxisModel.getModel('axisTick'); + + var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length'); + + var lines = zrUtil.map(ticksAngles, function (tickAngle) { + return new graphic.Line({ + shape: getAxisLineShape(polar, radiusExtent[1], radiusExtent[1] + tickLen, tickAngle) + }); + }); + this.group.add(graphic.mergePath( + lines, { + style: zrUtil.defaults( + tickModel.getModel('lineStyle').getLineStyle(), + { + stroke: angleAxisModel.get('axisLine.lineStyle.color') + } + ) + } + )); + }, + + /** + * @private + */ + _axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var axis = angleAxisModel.axis; + + var categoryData = angleAxisModel.get('data'); + + var labelModel = angleAxisModel.getModel('axisLabel'); + var axisTextStyleModel = labelModel.getModel('textStyle'); + + var labels = angleAxisModel.getFormattedLabels(); + + var labelMargin = labelModel.get('margin'); + var labelsAngles = axis.getLabelsCoords(); + + // Use length of ticksAngles because it may remove the last tick to avoid overlapping + for (var i = 0; i < ticksAngles.length; i++) { + var r = radiusExtent[1]; + var p = polar.coordToPoint([r + labelMargin, labelsAngles[i]]); + var cx = polar.cx; + var cy = polar.cy; + + var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 + ? 'center' : (p[0] > cx ? 'left' : 'right'); + var labelTextBaseline = Math.abs(p[1] - cy) / r < 0.3 + ? 'middle' : (p[1] > cy ? 'top' : 'bottom'); + + var textStyleModel = axisTextStyleModel; + if (categoryData && categoryData[i] && categoryData[i].textStyle) { + textStyleModel = new Model( + categoryData[i].textStyle, axisTextStyleModel + ); + } + this.group.add(new graphic.Text({ + style: { + x: p[0], + y: p[1], + fill: textStyleModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'), + text: labels[i], + textAlign: labelTextAlign, + textVerticalAlign: labelTextBaseline, + textFont: textStyleModel.getFont() + }, + silent: true + })); + } + }, + + /** + * @private + */ + _splitLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var splitLineModel = angleAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var splitLines = []; + + for (var i = 0; i < ticksAngles.length; i++) { + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Line({ + shape: getAxisLineShape(polar, radiusExtent[0], radiusExtent[1], ticksAngles[i]) + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(graphic.mergePath(splitLines[i], { + style: zrUtil.defaults({ + stroke: lineColors[i % lineColors.length] + }, lineStyleModel.getLineStyle()), + silent: true, + z: angleAxisModel.get('z') + })); + } + }, + + /** + * @private + */ + _splitArea: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + + var splitAreaModel = angleAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + + var splitAreas = []; + + var RADIAN = Math.PI / 180; + var prevAngle = -ticksAngles[0] * RADIAN; + var r0 = Math.min(radiusExtent[0], radiusExtent[1]); + var r1 = Math.max(radiusExtent[0], radiusExtent[1]); + + var clockwise = angleAxisModel.get('clockwise'); + + for (var i = 1; i < ticksAngles.length; i++) { + var colorIndex = (lineCount++) % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: r0, + r: r1, + startAngle: prevAngle, + endAngle: -ticksAngles[i] * RADIAN, + clockwise: clockwise + }, + silent: true + })); + prevAngle = -ticksAngles[i] * RADIAN; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(graphic.mergePath(splitAreas[i], { + style: zrUtil.defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }); + + +/***/ }, +/* 292 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(284); + + __webpack_require__(293); + + +/***/ }, +/* 293 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var AxisBuilder = __webpack_require__(132); + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + var selfBuilderAttrs = [ + 'splitLine', 'splitArea' + ]; + + __webpack_require__(1).extendComponentView({ + + type: 'radiusAxis', + + render: function (radiusAxisModel, ecModel) { + this.group.removeAll(); + if (!radiusAxisModel.get('show')) { + return; + } + var polarModel = ecModel.getComponent('polar', radiusAxisModel.get('polarIndex')); + var angleAxis = polarModel.coordinateSystem.getAngleAxis(); + var radiusAxis = radiusAxisModel.axis; + var polar = polarModel.coordinateSystem; + var ticksCoords = radiusAxis.getTicksCoords(); + var axisAngle = angleAxis.getExtent()[0]; + var radiusExtent = radiusAxis.getExtent(); + + var layout = layoutAxis(polar, radiusAxisModel, axisAngle); + var axisBuilder = new AxisBuilder(radiusAxisModel, layout); + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + + zrUtil.each(selfBuilderAttrs, function (name) { + if (radiusAxisModel.get(name +'.show')) { + this['_' + name](radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords); + } + }, this); + }, + + /** + * @private + */ + _splitLine: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + var splitLineModel = radiusAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var splitLines = []; + + for (var i = 0; i < ticksCoords.length; i++) { + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: ticksCoords[i] + }, + silent: true + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(graphic.mergePath(splitLines[i], { + style: zrUtil.defaults({ + stroke: lineColors[i % lineColors.length], + fill: null + }, lineStyleModel.getLineStyle()), + silent: true + })); + } + }, + + /** + * @private + */ + _splitArea: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + + var splitAreaModel = radiusAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + + var splitAreas = []; + + var prevRadius = ticksCoords[0]; + for (var i = 1; i < ticksCoords.length; i++) { + var colorIndex = (lineCount++) % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: prevRadius, + r: ticksCoords[i], + startAngle: 0, + endAngle: Math.PI * 2 + }, + silent: true + })); + prevRadius = ticksCoords[i]; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(graphic.mergePath(splitAreas[i], { + style: zrUtil.defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }); + + /** + * @inner + */ + function layoutAxis(polar, radiusAxisModel, axisAngle) { + return { + position: [polar.cx, polar.cy], + rotation: axisAngle / 180 * Math.PI, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1, + labelRotation: radiusAxisModel.getModel('axisLabel').get('rotate'), + // Over splitLine and splitArea + z2: 1 + }; + } + + +/***/ }, +/* 294 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(295); + + __webpack_require__(163); + + __webpack_require__(296); + + __webpack_require__(176); + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + function makeAction(method, actionInfo) { + actionInfo.update = 'updateView'; + echarts.registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + + ecModel.eachComponent( + { mainType: 'geo', query: payload}, + function (geoModel) { + geoModel[method](payload.name); + var geo = geoModel.coordinateSystem; + zrUtil.each(geo.regions, function (region) { + selected[region.name] = geoModel.isSelected(region.name) || false; + }); + } + ); + + return { + selected: selected, + name: payload.name + } + }); + } + + makeAction('toggleSelected', { + type: 'geoToggleSelect', + event: 'geoselectchanged' + }); + makeAction('select', { + type: 'geoSelect', + event: 'geoselected' + }); + makeAction('unSelect', { + type: 'geoUnSelect', + event: 'geounselected' + }); + + +/***/ }, +/* 295 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var selectableMixin = __webpack_require__(140); + + var geoCreator = __webpack_require__(163); + + var GeoModel = ComponentModel.extend({ + + type: 'geo', + + /** + * @type {module:echarts/coord/geo/Geo} + */ + coordinateSystem: null, + + layoutMode: 'box', + + init: function (option) { + ComponentModel.prototype.init.apply(this, arguments); + + // Default label emphasis `position` and `show` + modelUtil.defaultEmphasis( + option.label, ['position', 'show', 'textStyle', 'distance', 'formatter'] + ); + }, + + optionUpdated: function () { + var option = this.option; + var self = this; + + option.regions = geoCreator.getFilledRegions(option.regions, option.map); + + this._optionModelMap = zrUtil.reduce(option.regions || [], function (obj, regionOpt) { + if (regionOpt.name) { + obj[regionOpt.name] = new Model(regionOpt, self); + } + return obj; + }, {}); + + this.updateSelectedMap(option.regions); + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + show: true, + + left: 'center', + + top: 'center', + + + // width:, + // height:, + // right + // bottom + + // Aspect is width / height. Inited to be geoJson bbox aspect + // This parameter is used for scale this aspect + aspectScale: 0.75, + + ///// Layout with center and size + // If you wan't to put map in a fixed size box with right aspect ratio + // This two properties may more conveninet + // layoutCenter: [50%, 50%] + // layoutSize: 100 + + + silent: false, + + // Map type + map: '', + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + // selectedMode: false + + label: { + normal: { + show: false, + textStyle: { + color: '#000' + } + }, + emphasis: { + show: true, + textStyle: { + color: 'rgb(100,0,0)' + } + } + }, + + itemStyle: { + normal: { + // color: 各异, + borderWidth: 0.5, + borderColor: '#444', + color: '#eee' + }, + emphasis: { // 也是选中样式 + color: 'rgba(255,215,0,0.8)' + } + }, + + regions: [] + }, + + /** + * Get model of region + * @param {string} name + * @return {module:echarts/model/Model} + */ + getRegionModel: function (name) { + return this._optionModelMap[name]; + }, + + /** + * Format label + * @param {string} name Region name + * @param {string} [status='normal'] 'normal' or 'emphasis' + * @return {string} + */ + getFormattedLabel: function (name, status) { + var formatter = this.get('label.' + status + '.formatter'); + var params = { + name: name + }; + if (typeof formatter === 'function') { + params.status = status; + return formatter(params); + } + else if (typeof formatter === 'string') { + return formatter.replace('{a}', params.seriesName); + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + } + }); + + zrUtil.mixin(GeoModel, selectableMixin); + + module.exports = GeoModel; + + +/***/ }, +/* 296 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var MapDraw = __webpack_require__(173); + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'geo', + + init: function (ecModel, api) { + var mapDraw = new MapDraw(api, true); + this._mapDraw = mapDraw; + + this.group.add(mapDraw.group); + }, + + render: function (geoModel, ecModel, api, payload) { + // Not render if it is an toggleSelect action from self + if (payload && payload.type === 'geoToggleSelect' + && payload.from === this.uid + ) { + return; + } + + var mapDraw = this._mapDraw; + if (geoModel.get('show')) { + mapDraw.draw(geoModel, ecModel, api, this, payload); + } + else { + this._mapDraw.group.removeAll(); + } + + this.group.silent = geoModel.get('silent'); + } + }); + + +/***/ }, +/* 297 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(298); + __webpack_require__(301); + __webpack_require__(302); + + var echarts = __webpack_require__(1); + + echarts.extendComponentView({ + type: 'single' + }); + + + +/***/ }, +/* 298 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Single coordinate system creator. + */ + + + var Single = __webpack_require__(299); + + /** + * Create single coordinate system and inject it into seriesModel. + * + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ + function create(ecModel, api) { + var singles = []; + + ecModel.eachComponent('singleAxis', function(axisModel, idx) { + + var single = new Single(axisModel, ecModel, api); + single.name = 'single_' + idx; + single.resize(axisModel, api); + axisModel.coordinateSystem = single; + singles.push(single); + + }); + + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'singleAxis') { + var singleAxisModel = ecModel.queryComponents({ + mainType: 'singleAxis', + index: seriesModel.get('singleAxisIndex'), + id: seriesModel.get('singleAxisId') + })[0]; + seriesModel.coordinateSystem = singleAxisModel.coordinateSystem; + } + }); + + return singles; + } + + __webpack_require__(26).register('single', { + create: create, + dimensions: Single.prototype.dimensions + }); + + +/***/ }, +/* 299 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Single coordinates system. + */ + + + var SingleAxis = __webpack_require__(300); + var axisHelper = __webpack_require__(114); + var layout = __webpack_require__(21); + + /** + * Create a single coordinates system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + function Single(axisModel, ecModel, api) { + + /** + * @type {string} + * @readOnly + */ + this.dimension = 'x'; + + /** + * Add it just for draw tooltip. + * + * @type {Array.} + * @readOnly + */ + this.dimensions = ['x']; + + /** + * @private + * @type {module:echarts/coord/single/SingleAxis}. + */ + this._axis = null; + + /** + * @private + * @type {module:zrender/core/BoundingRect} + */ + this._rect; + + this._init(axisModel, ecModel, api); + + /** + * @type {module:echarts/coord/single/AxisModel} + */ + this._model = axisModel; + } + + Single.prototype = { + + type: 'singleAxis', + + constructor: Single, + + /** + * Initialize single coordinate system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @private + */ + _init: function (axisModel, ecModel, api) { + + var dim = this.dimension; + + var axis = new SingleAxis( + dim, + axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisModel.get('position') + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + axis.orient = axisModel.get('orient'); + + axisModel.axis = axis; + axis.model = axisModel; + this._axis = axis; + }, + + /** + * Update axis scale after data processed + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + update: function (ecModel, api) { + this._updateAxisFromSeries(ecModel); + }, + + /** + * Update the axis extent from series. + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + _updateAxisFromSeries: function (ecModel) { + + ecModel.eachSeries(function (seriesModel) { + + var data = seriesModel.getData(); + var dim = this.dimension; + this._axis.scale.unionExtent( + data.getDataExtent(seriesModel.coordDimToDataDim(dim)) + ); + axisHelper.niceScaleExtent(this._axis, this._axis.model); + }, this); + }, + + /** + * Resize the single coordinate system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/ExtensionAPI} api + */ + resize: function (axisModel, api) { + this._rect = layout.getLayoutRect( + { + left: axisModel.get('left'), + top: axisModel.get('top'), + right: axisModel.get('right'), + bottom: axisModel.get('bottom'), + width: axisModel.get('width'), + height: axisModel.get('height') + }, + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + this._adjustAxis(); + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getRect: function () { + return this._rect; + }, + + /** + * @private + */ + _adjustAxis: function () { + + var rect = this._rect; + var axis = this._axis; + + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, rect.width] : [0, rect.height]; + var idx = axis.reverse ? 1 : 0; + + axis.setExtent(extent[idx], extent[1 - idx]); + + this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y); + + }, + + /** + * @param {module:echarts/coord/single/SingleAxis} axis + * @param {number} coordBase + */ + _updateAxisTransform: function (axis, coordBase) { + + var axisExtent = axis.getExtent(); + var extentSum = axisExtent[0] + axisExtent[1]; + var isHorizontal = axis.isHorizontal(); + + axis.toGlobalCoord = isHorizontal ? + function (coord) { + return coord + coordBase; + } : + function (coord) { + return extentSum - coord + coordBase; + }; + + axis.toLocalCoord = isHorizontal ? + function (coord) { + return coord - coordBase; + } : + function (coord) { + return extentSum - coord + coordBase; + }; + }, + + /** + * Get axis. + * + * @return {module:echarts/coord/single/SingleAxis} + */ + getAxis: function () { + return this._axis; + }, + + /** + * Get axis, add it just for draw tooltip. + * + * @return {[type]} [description] + */ + getBaseAxis: function () { + return this._axis; + }, + + /** + * If contain point. + * + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var rect = this.getRect(); + var axis = this.getAxis(); + var orient = axis.orient; + if (orient === 'horizontal') { + return axis.contain(axis.toLocalCoord(point[0])) + && (point[1] >= rect.y && point[1] <= (rect.y + rect.height)); + } + else { + return axis.contain(axis.toLocalCoord(point[1])) + && (point[0] >= rect.y && point[0] <= (rect.y + rect.height)); + } + }, + + /** + * @param {Array.} point + * @return {Array.} + */ + pointToData: function (point) { + var axis = this.getAxis(); + return [axis.coordToData(axis.toLocalCoord( + point[axis.orient === 'horizontal' ? 0 : 1] + ))]; + }, + + /** + * Convert the series data to concrete point. + * + * @param {number|Array.} val + * @return {Array.} + */ + dataToPoint: function (val) { + var axis = this.getAxis(); + var rect = this.getRect(); + var pt = []; + var idx = axis.orient === 'horizontal' ? 0 : 1; + pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val)); + pt[1 - idx] = idx === 0 ? (rect.y + rect.height / 2) : (rect.x + rect.width / 2); + return pt; + } + }; + + module.exports = Single; + + + +/***/ }, +/* 300 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisHelper = __webpack_require__(114); + + /** + * @constructor module:echarts/coord/single/SingleAxis + * @extends {module:echarts/coord/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var SingleAxis = function (dim, scale, coordExtent, axisType, position) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * Axis position + * - 'top' + * - 'bottom' + * - 'left' + * - 'right' + * @type {string} + */ + this.position = position || 'bottom'; + + /** + * Axis orient + * - 'horizontal' + * - 'vertical' + * @type {[type]} + */ + this.orient = null; + + /** + * @type {number} + */ + this._labelInterval = null; + + }; + + SingleAxis.prototype = { + + constructor: SingleAxis, + + /** + * Axis model + * @type {module:echarts/coord/single/AxisModel} + */ + model: null, + + /** + * Judge the orient of the axis. + * @return {boolean} + */ + isHorizontal: function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + + }, + + /** + * Get interval of the axis label. + * @return {number} + */ + getLabelInterval: function () { + var labelInterval = this._labelInterval; + if (!labelInterval) { + var axisModel = this.model; + var labelModel = axisModel.getModel('axisLabel'); + var interval = labelModel.get('interval'); + if (!(this.type === 'category' && interval === 'auto')) { + + labelInterval = this._labelInterval = interval === 'auto' ? 0 : interval; + return labelInterval; + } + labelInterval = this._labelInterval = + axisHelper.getAxisLabelInterval( + zrUtil.map(this.scale.getTicks(), this.dataToCoord, this), + axisModel.getFormattedLabels(), + labelModel.getModel('textStyle').getFont(), + this.isHorizontal() + ); + } + return labelInterval; + }, + + /** + * Convert the local coord(processed by dataToCoord()) + * to global coord(concrete pixel coord). + * designated by module:echarts/coord/single/Single. + * @type {Function} + */ + toGlobalCoord: null, + + /** + * Convert the global coord to local coord. + * designated by module:echarts/coord/single/Single. + * @type {Function} + */ + toLocalCoord: null + + }; + + zrUtil.inherits(SingleAxis, Axis); + + module.exports = SingleAxis; + + +/***/ }, +/* 301 */ +/***/ function(module, exports, __webpack_require__) { + + + + var AxisBuilder = __webpack_require__(132); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var getInterval = AxisBuilder.getInterval; + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick; + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + + var selfBuilderAttr = 'splitLine'; + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'singleAxis', + + render: function (axisModel, ecModel) { + + var group = this.group; + + group.removeAll(); + + var layout = axisLayout(axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + + group.add(axisBuilder.getGroup()); + + if (axisModel.get(selfBuilderAttr + '.show')) { + this['_' + selfBuilderAttr](axisModel, layout.labelInterval); + } + }, + + _splitLine: function(axisModel, labelInterval) { + var axis = axisModel.axis; + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineWidth = lineStyleModel.get('width'); + var lineColors = lineStyleModel.get('color'); + var lineInterval = getInterval(splitLineModel, labelInterval); + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var gridRect = axisModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var splitLines = []; + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords(); + + var p1 = []; + var p2 = []; + + for (var i = 0; i < ticksCoords.length; ++i) { + if (ifIgnoreOnTick(axis, i, lineInterval)) { + continue; + } + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } + else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Line( + graphic.subPixelOptimizeLine({ + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: { + lineWidth: lineWidth + }, + silent: true + }))); + } + + for (var i = 0; i < splitLines.length; ++i) { + this.group.add(graphic.mergePath(splitLines[i], { + style: { + stroke: lineColors[i % lineColors.length], + lineDash: lineStyleModel.getLineDash(), + lineWidth: lineWidth + }, + silent: true + })); + } + } + }); + + function axisLayout(axisModel) { + + var single = axisModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + + var axisPosition = axis.position; + var orient = axis.orient; + + var rect = single.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + + var positionMap = { + horizontal: {top: rectBound[2], bottom: rectBound[3]}, + vertical: {left: rectBound[0], right: rectBound[1]} + }; + + layout.position = [ + orient === 'vertical' + ? positionMap.vertical[axisPosition] + : rectBound[0], + orient === 'horizontal' + ? positionMap.horizontal[axisPosition] + : rectBound[3] + ]; + + var r = {horizontal: 0, vertical: 1}; + layout.rotation = Math.PI / 2 * r[orient]; + + var directionMap = {top: -1, bottom: 1, right: 1, left: -1}; + + layout.labelDirection = layout.tickDirection + = layout.nameDirection + = directionMap[axisPosition]; + + if (axisModel.getModel('axisTick').get('inside')) { + layout.tickDirection = -layout.tickDirection; + } + + if (axisModel.getModel('axisLabel').get('inside')) { + layout.labelDirection = -layout.labelDirection; + } + + var labelRotation = axisModel.getModel('axisLabel').get('rotate'); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + + layout.labelInterval = axis.getLabelInterval(); + + layout.z2 = 1; + + return layout; + } + + module.exports = AxisView; + + + +/***/ }, +/* 302 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentModel = __webpack_require__(19); + var axisModelCreator = __webpack_require__(127); + var zrUtil = __webpack_require__(4); + + var AxisModel = ComponentModel.extend({ + + type: 'singleAxis', + + layoutMode: 'box', + + /** + * @type {module:echarts/coord/single/SingleAxis} + */ + axis: null, + + /** + * @type {module:echarts/coord/single/Single} + */ + coordinateSystem: null + + }); + + var defaultOption = { + + left: '5%', + top: '5%', + right: '5%', + bottom: '5%', + + type: 'value', + + position: 'bottom', + + orient: 'horizontal', + + axisLine: { + show: true, + lineStyle: { + width: 2, + type: 'solid' + } + }, + + axisTick: { + show: true, + length: 6, + lineStyle: { + width: 2 + } + }, + + axisLabel: { + show: true, + interval: 'auto' + }, + + splitLine: { + show: true, + lineStyle: { + type: 'dashed', + opacity: 0.2 + } + } + }; + + function getAxisType(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); + } + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + axisModelCreator('single', AxisModel, getAxisType, defaultOption); + + module.exports = AxisModel; + + +/***/ }, +/* 303 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Brush component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(304) + ); + + __webpack_require__(305); + __webpack_require__(310); + __webpack_require__(311); + __webpack_require__(312); + + __webpack_require__(313); + + + +/***/ }, +/* 304 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file brush preprocessor + */ + + + var zrUtil = __webpack_require__(4); + + var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear']; + + module.exports = function (option, isNew) { + var brushComponents = option && option.brush; + if (!zrUtil.isArray(brushComponents)) { + brushComponents = brushComponents ? [brushComponents] : []; + } + + if (!brushComponents.length) { + return; + } + + var brushComponentSpecifiedBtns = []; + + zrUtil.each(brushComponents, function (brushOpt) { + var tbs = brushOpt.hasOwnProperty('toolbox') + ? brushOpt.toolbox : []; + + if (tbs instanceof Array) { + brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs); + } + }); + + var toolbox = option && option.toolbox; + + if (zrUtil.isArray(toolbox)) { + toolbox = toolbox[0]; + } + if (!toolbox) { + toolbox = {feature: {}}; + option.toolbox = [toolbox]; + } + + var toolboxFeature = (toolbox.feature || (toolbox.feature = {})); + var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {}); + var brushTypes = toolboxBrush.type || (toolboxBrush.type = []); + + brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns); + + removeDuplicate(brushTypes); + + if (isNew && !brushTypes.length) { + brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS); + } + }; + + function removeDuplicate(arr) { + var map = {}; + zrUtil.each(arr, function (val) { + map[val] = 1; + }); + arr.length = 0; + zrUtil.each(map, function (flag, val) { + arr.push(val); + }); + } + + + +/***/ }, +/* 305 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush visual coding. + */ + + + var echarts = __webpack_require__(1); + var visualSolution = __webpack_require__(306); + var zrUtil = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var selector = __webpack_require__(307); + var throttle = __webpack_require__(308); + var brushHelper = __webpack_require__(309); + + var STATE_LIST = ['inBrush', 'outOfBrush']; + var DISPATCH_METHOD = '__ecBrushSelect'; + var DISPATCH_FLAG = '__ecInBrushSelectEvent'; + var PRIORITY_BRUSH = echarts.PRIORITY.VISUAL.BRUSH; + + /** + * Layout for visual, the priority higher than other layout, and before brush visual. + */ + echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) { + ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { + + payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption( + payload.key === 'brush' ? payload.brushOption : {brushType: false} + ); + + brushModel.coordInfoList = brushHelper.makeCoordInfoList(brushModel.option, ecModel); + + brushHelper.parseInputRanges(brushModel, ecModel); + }); + }); + + /** + * Register the visual encoding if this modules required. + */ + echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) { + + var brushSelected = []; + var throttleType; + var throttleDelay; + + ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) { + + var thisBrushSelected = { + brushId: brushModel.id, + brushIndex: brushIndex, + brushName: brushModel.name, + areas: zrUtil.clone(brushModel.areas), + selected: [] + }; + // Every brush component exists in event params, convenient + // for user to find by index. + brushSelected.push(thisBrushSelected); + + var brushOption = brushModel.option; + var brushLink = brushOption.brushLink; + var linkedSeriesMap = []; + var selectedDataIndexForLink = []; + var rangeInfoBySeries = []; + var hasBrushExists = 0; + + if (!brushIndex) { // Only the first throttle setting works. + throttleType = brushOption.throttleType; + throttleDelay = brushOption.throttleDelay; + } + + // Add boundingRect and selectors to range. + var areas = zrUtil.map(brushModel.areas, function (area) { + return bindSelector( + zrUtil.defaults( + {boundingRect: boundingRectBuilders[area.brushType](area)}, + area + ) + ); + }); + + var visualMappings = visualSolution.createVisualMappings( + brushModel.option, STATE_LIST, function (mappingOption) { + mappingOption.mappingMethod = 'fixed'; + } + ); + + zrUtil.isArray(brushLink) && zrUtil.each(brushLink, function (seriesIndex) { + linkedSeriesMap[seriesIndex] = 1; + }); + + function linkOthers(seriesIndex) { + return brushLink === 'all' || linkedSeriesMap[seriesIndex]; + } + + // If no supported brush or no brush on the series, + // all visuals should be in original state. + function brushed(rangeInfoList) { + return !!rangeInfoList.length; + } + + /** + * Logic for each series: (If the logic has to be modified one day, do it carefully!) + * + * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers ) => StepA: ┬record, ┬ StepB: ┬visualByRecord. + * !brushed┘ ├hasBrushExist ┤ └nothing,┘ ├visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( !brushed && ┬hasBrushExist ┬ && linkOthers ) => StepA: nothing, StepB: ┬visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( brushed ┬ && !linkOthers ) => StepA: nothing, StepB: ┬visualByCheck. + * !brushed┘ └nothing. + * ( !brushed && !linkOthers ) => StepA: nothing, StepB: nothing. + */ + + // Step A + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var rangeInfoList = rangeInfoBySeries[seriesIndex] = []; + + seriesModel.subType === 'parallel' + ? stepAParallel(seriesModel, seriesIndex, rangeInfoList) + : stepAOthers(seriesModel, seriesIndex, rangeInfoList); + }); + + function stepAParallel(seriesModel, seriesIndex) { + var coordSys = seriesModel.coordinateSystem; + hasBrushExists |= coordSys.hasAxisbrushed(); + + linkOthers(seriesIndex) && coordSys.eachActiveState( + seriesModel.getData(), + function (activeState, dataIndex) { + activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1); + } + ); + } + + function stepAOthers(seriesModel, seriesIndex, rangeInfoList) { + var selectorsByBrushType = getSelectorsByBrushType(seriesModel); + if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) { + return; + } + + zrUtil.each(areas, function (area) { + selectorsByBrushType[area.brushType] + && brushHelper.controlSeries(area, brushModel, seriesModel) + && rangeInfoList.push(area); + hasBrushExists |= brushed(rangeInfoList); + }); + + if (linkOthers(seriesIndex) && brushed(rangeInfoList)) { + var data = seriesModel.getData(); + data.each(function (dataIndex) { + if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) { + selectedDataIndexForLink[dataIndex] = 1; + } + }); + } + } + + // Step B + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var seriesBrushSelected = { + seriesId: seriesModel.id, + seriesIndex: seriesIndex, + seriesName: seriesModel.name, + dataIndex: [] + }; + // Every series exists in event params, convenient + // for user to find series by seriesIndex. + thisBrushSelected.selected.push(seriesBrushSelected); + + var selectorsByBrushType = getSelectorsByBrushType(seriesModel); + var rangeInfoList = rangeInfoBySeries[seriesIndex]; + + var data = seriesModel.getData(); + var getValueState = linkOthers(seriesIndex) + ? function (dataIndex) { + return selectedDataIndexForLink[dataIndex] + ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') + : 'outOfBrush'; + } + : function (dataIndex) { + return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) + ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') + : 'outOfBrush'; + }; + + // If no supported brush or no brush, all visuals are in original state. + (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) + && visualSolution.applyVisual( + STATE_LIST, visualMappings, data, getValueState + ); + }); + + }); + + dispatchAction(api, throttleType, throttleDelay, brushSelected, payload); + }); + + function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) { + // This event will not be triggered when `setOpion`, otherwise dead lock may + // triggered when do `setOption` in event listener, which we do not find + // satisfactory way to solve yet. Some considered resolutions: + // (a) Diff with prevoius selected data ant only trigger event when changed. + // But store previous data and diff precisely (i.e., not only by dataIndex, but + // also detect value changes in selected data) might bring complexity or fragility. + // (b) Use spectial param like `silent` to suppress event triggering. + // But such kind of volatile param may be weird in `setOption`. + if (!payload) { + return; + } + + var zr = api.getZr(); + if (zr[DISPATCH_FLAG]) { + return; + } + + if (!zr[DISPATCH_METHOD]) { + zr[DISPATCH_METHOD] = doDispatch; + } + + var fn = throttle.createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType); + + fn(api, brushSelected); + } + + function doDispatch(api, brushSelected) { + if (!api.isDisposed()) { + var zr = api.getZr(); + zr[DISPATCH_FLAG] = true; + api.dispatchAction({ + type: 'brushSelect', + batch: brushSelected + }); + zr[DISPATCH_FLAG] = false; + } + } + + function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) { + var itemLayout = data.getItemLayout(dataIndex); + for (var i = 0, len = rangeInfoList.length; i < len; i++) { + var area = rangeInfoList[i]; + if (selectorsByBrushType[area.brushType]( + itemLayout, area.selectors, area + )) { + return true; + } + } + } + + function getSelectorsByBrushType(seriesModel) { + var brushSelector = seriesModel.brushSelector; + if (zrUtil.isString(brushSelector)) { + var sels = []; + zrUtil.each(selector, function (selectorsByElementType, brushType) { + sels[brushType] = selectorsByElementType[brushSelector]; + }); + return sels; + } + else if (zrUtil.isFunction(brushSelector)) { + var bSelector = {}; + zrUtil.each(selector, function (sel, brushType) { + bSelector[brushType] = brushSelector; + }); + return bSelector; + } + return brushSelector; + } + + function brushModelNotControll(brushModel, seriesIndex) { + var seriesIndices = brushModel.option.seriesIndex; + return seriesIndices != null + && seriesIndices !== 'all' + && ( + zrUtil.isArray(seriesIndices) + ? zrUtil.indexOf(seriesIndices, seriesIndex) < 0 + : seriesIndex !== seriesIndices + ); + } + + function bindSelector(area) { + var selectors = area.selectors = {}; + zrUtil.each(selector[area.brushType], function (selFn, elType) { + // Do not use function binding or curry for performance. + selectors[elType] = function (itemLayout) { + return selFn(itemLayout, selectors, area); + }; + }); + return area; + } + + var boundingRectBuilders = { + + lineX: zrUtil.noop, + + lineY: zrUtil.noop, + + rect: function (area) { + return getBoundingRectFromMinMax(area.range); + }, + + polygon: function (area) { + var minMax; + var range = area.range; + + for (var i = 0, len = range.length; i < len; i++) { + minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]]; + var rg = range[i]; + rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]); + rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]); + rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]); + rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]); + } + + return minMax && getBoundingRectFromMinMax(minMax); + } + }; + + function getBoundingRectFromMinMax(minMax) { + return new BoundingRect( + minMax[0][0], + minMax[1][0], + minMax[0][1] - minMax[0][0], + minMax[1][1] - minMax[1][0] + ); + } + + + +/***/ }, +/* 306 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual solution, for consistent option specification. + */ + + + var zrUtil = __webpack_require__(4); + var VisualMapping = __webpack_require__(192); + var each = zrUtil.each; + + function hasKeys(obj) { + if (obj) { + for (var name in obj){ + if (obj.hasOwnProperty(name)) { + return true; + } + } + } + } + + var visualSolution = { + + /** + * @param {Object} option + * @param {Array.} stateList + * @param {Function} [supplementVisualOption] + * @return {Object} visualMappings > + */ + createVisualMappings: function (option, stateList, supplementVisualOption) { + var visualMappings = {}; + + each(stateList, function (state) { + var mappings = visualMappings[state] = createMappings(); + + each(option[state], function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + var mappingOption = { + type: visualType, + visual: visualData + }; + supplementVisualOption && supplementVisualOption(mappingOption, state); + mappings[visualType] = new VisualMapping(mappingOption); + + // Prepare a alpha for opacity, for some case that opacity + // is not supported, such as rendering using gradient color. + if (visualType === 'opacity') { + mappingOption = zrUtil.clone(mappingOption); + mappingOption.type = 'colorAlpha'; + mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption); + } + }); + }); + + return visualMappings; + + function createMappings() { + var Creater = function () {}; + // Make sure hidden fields will not be visited by + // object iteration (with hasOwnProperty checking). + Creater.prototype.__hidden = Creater.prototype; + var obj = new Creater(); + return obj; + } + }, + + /** + * @param {Object} thisOption + * @param {Object} newOption + * @param {Array.} keys + */ + replaceVisualOption: function (thisOption, newOption, keys) { + // Visual attributes merge is not supported, otherwise it + // brings overcomplicated merge logic. See #2853. So if + // newOption has anyone of these keys, all of these keys + // will be reset. Otherwise, all keys remain. + var has; + zrUtil.each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + has = true; + } + }); + has && zrUtil.each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + thisOption[key] = zrUtil.clone(newOption[key]); + } + else { + delete thisOption[key]; + } + }); + }, + + /** + * @param {Array.} stateList + * @param {Object} visualMappings > + * @param {module:echarts/data/List} list + * @param {Function} getValueState param: valueOrIndex, return: state. + * @param {object} [scope] Scope for getValueState + * @param {string} [dimension] Concrete dimension, if used. + */ + applyVisual: function (stateList, visualMappings, data, getValueState, scope, dimension) { + var visualTypesMap = {}; + zrUtil.each(stateList, function (state) { + var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); + visualTypesMap[state] = visualTypes; + }); + + var dataIndex; + + function getVisual(key) { + return data.getItemVisual(dataIndex, key); + } + + function setVisual(key, value) { + data.setItemVisual(dataIndex, key, value); + } + + if (dimension == null) { + data.each(eachItem, true); + } + else { + data.each([dimension], eachItem, true); + } + + function eachItem(valueOrIndex, index) { + dataIndex = dimension == null ? valueOrIndex : index; + var valueState = getValueState.call(scope, valueOrIndex); + var mappings = visualMappings[valueState]; + var visualTypes = visualTypesMap[valueState]; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + mappings[type] && mappings[type].applyVisual( + valueOrIndex, getVisual, setVisual + ); + } + } + } + }; + + module.exports = visualSolution; + + + +/***/ }, +/* 307 */ +/***/ function(module, exports, __webpack_require__) { + + + + var polygonContain = __webpack_require__(167).contain; + var BoundingRect = __webpack_require__(9); + + // Key of the first level is brushType: `line`, `rect`, `polygon`. + // Key of the second level is chart element type: `point`, `rect`. + // See moudule:echarts/component/helper/BrushController + // function param: + // {Object} itemLayout fetch from data.getItemLayout(dataIndex) + // {Object} selectors {point: selector, rect: selector, ...} + // {Object} area {range: [[], [], ..], boudingRect} + // function return: + // {boolean} Whether in the given brush. + var selector = { + lineX: getLineSelectors(0), + lineY: getLineSelectors(1), + rect: { + point: function (itemLayout, selectors, area) { + return area.boundingRect.contain(itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + return area.boundingRect.intersect(makeBoundingRect(itemLayout)); + } + }, + polygon: { + point: function (itemLayout, selectors, area) { + return area.boundingRect.contain(itemLayout[0], itemLayout[1]) + && polygonContain(area.range, itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + // FIXME + // 随意写的,没有考察过效率。 + var points = area.range; + + if (points.length <= 1) { + return false; + } + + var x = itemLayout.x; + var y = itemLayout.y; + var width = itemLayout.width; + var height = itemLayout.height; + var p = points[0]; + + if (polygonContain(points, x, y) + || polygonContain(points, x + width, y) + || polygonContain(points, x, y + height) + || polygonContain(points, x + width, y + height) + || makeBoundingRect(itemLayout).contain(p[0], p[1]) + || lineIntersectPolygon(x, y, x + width, y, points) + || lineIntersectPolygon(x, y, x, y + height, points) + || lineIntersectPolygon(x + width, y, x + width, y + height, points) + || lineIntersectPolygon(x, y + height, x + width, y + height, points) + ) { + return true; + } + } + } + }; + + function getLineSelectors(xyIndex) { + var xy = ['x', 'y']; + var wh = ['width', 'height']; + + return { + point: function (itemLayout, selectors, area) { + var range = area.range; + var p = itemLayout[xyIndex]; + return inLineRange(p, range); + }, + rect: function (itemLayout, selectors, area) { + var range = area.range; + return inLineRange(itemLayout[xy[xyIndex]], range) + || inLineRange(itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]], range); + } + }; + } + + function inLineRange(p, range) { + return range[0] <= p && p <= range[1]; + } + + // FIXME + // 随意写的,没考察过效率。 + function lineIntersectPolygon(lx, ly, l2x, l2y, points) { + for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) { + var p = points[i]; + if (lineIntersect(lx, ly, l2x, l2y, p[0], p[1], p2[0], p2[1])) { + return true; + } + p2 = p; + } + } + + // Code from with some fix. + // See + function lineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { + var delta = determinant(a2x - a1x, b1x - b2x, a2y - a1y, b1y - b2y); + if (nearZero(delta)) { // parallel + return false; + } + var namenda = determinant(b1x - a1x, b1x - b2x, b1y - a1y, b1y - b2y) / delta; + if (namenda < 0 || namenda > 1) { + return false; + } + var miu = determinant(a2x - a1x, b1x - a1x, a2y - a1y, b1y - a1y) / delta; + if (miu < 0 || miu > 1) { + return false; + } + return true; + } + + function nearZero(val) { + return val <= (1e-6) && val >= -(1e-6); + } + + function determinant(v1, v2, v3, v4) { + return v1 * v4 - v2 * v3; + } + + function makeBoundingRect(itemLayout) { + var x = itemLayout.x; + var y = itemLayout.y; + var width = itemLayout.width; + var height = itemLayout.height; + + // width and height might be negative. + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + return new BoundingRect(x, y, width, height); + } + + module.exports = selector; + + + +/***/ }, +/* 308 */ +/***/ function(module, exports) { + + + + var lib = {}; + + var ORIGIN_METHOD = '\0__throttleOriginMethod'; + var RATE = '\0__throttleRate'; + var THROTTLE_TYPE = '\0__throttleType'; + + /** + * @public + * @param {(Function)} fn + * @param {number} [delay=0] Unit: ms. + * @param {boolean} [debounce=false] + * true: If call interval less than `delay`, only the last call works. + * false: If call interval less than `delay, call works on fixed rate. + * @return {(Function)} throttled fn. + */ + lib.throttle = function (fn, delay, debounce) { + + var currCall; + var lastCall = 0; + var lastExec = 0; + var timer = null; + var diff; + var scope; + var args; + + delay = delay || 0; + + function exec() { + lastExec = (new Date()).getTime(); + timer = null; + fn.apply(scope, args || []); + } + + var cb = function () { + currCall = (new Date()).getTime(); + scope = this; + args = arguments; + diff = currCall - (debounce ? lastCall : lastExec) - delay; + + clearTimeout(timer); + + if (debounce) { + timer = setTimeout(exec, delay); + } + else { + if (diff >= 0) { + exec(); + } + else { + timer = setTimeout(exec, -diff); + } + } + + lastCall = currCall; + }; + + /** + * Clear throttle. + * @public + */ + cb.clear = function () { + if (timer) { + clearTimeout(timer); + timer = null; + } + }; + + return cb; + }; + + /** + * Create throttle method or update throttle rate. + * + * @example + * ComponentView.prototype.render = function () { + * ... + * throttle.createOrUpdate( + * this, + * '_dispatchAction', + * this.model.get('throttle'), + * 'fixRate' + * ); + * }; + * ComponentView.prototype.remove = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * ComponentView.prototype.dispose = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * + * @public + * @param {Object} obj + * @param {string} fnAttr + * @param {number} [rate] + * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce' + * @return {Function} throttled function. + */ + lib.createOrUpdate = function (obj, fnAttr, rate, throttleType) { + var fn = obj[fnAttr]; + + if (!fn) { + return; + } + + var originFn = fn[ORIGIN_METHOD] || fn; + var lastThrottleType = fn[THROTTLE_TYPE]; + var lastRate = fn[RATE]; + + if (lastRate !== rate || lastThrottleType !== throttleType) { + if (rate == null || !throttleType) { + return (obj[fnAttr] = originFn); + } + + fn = obj[fnAttr] = lib.throttle( + originFn, rate, throttleType === 'debounce' + ); + fn[ORIGIN_METHOD] = originFn; + fn[THROTTLE_TYPE] = throttleType; + fn[RATE] = rate; + } + + return fn; + }; + + /** + * Clear throttle. Example see throttle.createOrUpdate. + * + * @public + * @param {Object} obj + * @param {string} fnAttr + */ + lib.clear = function (obj, fnAttr) { + var fn = obj[fnAttr]; + if (fn && fn[ORIGIN_METHOD]) { + obj[fnAttr] = fn[ORIGIN_METHOD]; + } + }; + + module.exports = lib; + + + +/***/ }, +/* 309 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + var each = zrUtil.each; + + var helper = {}; + + var COMPONENT_NAMES = ['geo', 'xAxis', 'yAxis']; + var PANEL_ID_SPLIT = '--'; + var COORD_CONVERTS = ['dataToPoint', 'pointToData']; + + helper.parseOutputRanges = function (areas, coordInfoList, ecModel, rangesCoordInfo) { + each(areas, function (area, index) { + var panelId = area.panelId; + + if (panelId) { + panelId = panelId.split(PANEL_ID_SPLIT); + + area[panelId[0] + 'Index'] = +panelId[1]; + + var coordInfo = findCoordInfo(area, coordInfoList); + area.coordRange = coordConvert[area.brushType]( + 1, coordInfo, area.range + ); + rangesCoordInfo && (rangesCoordInfo[index] = coordInfo); + } + }); + }; + + helper.parseInputRanges = function (brushModel, ecModel) { + each(brushModel.areas, function (area) { + var coordInfo = findCoordInfo(area, brushModel.coordInfoList); + + if (true) { + zrUtil.assert( + !coordInfo || coordInfo === true || area.coordRange, + 'coordRange must be specified when coord index specified.' + ); + zrUtil.assert( + !coordInfo || coordInfo !== true || area.range, + 'range must be specified.' + ); + } + + area.range = area.range || []; + + // convert coordRange to global range and set panelId. + if (coordInfo && coordInfo !== true) { + area.range = coordConvert[area.brushType]( + 0, coordInfo, area.coordRange + ); + area.panelId = coordInfo.panelId; + } + }); + }; + + helper.makePanelOpts = function (coordInfoList) { + var panelOpts = []; + + each(coordInfoList, function (coordInfo) { + var coordSys = coordInfo.coordSys; + var rect; + + if (coordInfo.geoIndex >= 0) { + rect = coordSys.getBoundingRect().clone(); + // geo roam and zoom transform + rect.applyTransform(graphic.getTransform(coordSys)); + } + else { // xAxis or yAxis + // grid is not Transformable. + rect = coordSys.grid.getRect().clone(); + } + + panelOpts.push({panelId: coordInfo.panelId, rect: rect}); + }); + + return panelOpts; + }; + + /** + * @param {Object} option {xAxisIndex, yAxisIndex, geoIndex} + * @param {module:echarts/model/Global} ecModel + * @return {Array.} coordInfoList + */ + helper.makeCoordInfoList = function (option, ecModel) { + var coordInfoList = []; + + each(COMPONENT_NAMES, function (componentName) { + var componentIndices = option[componentName + 'Index']; + if (componentIndices == null || componentIndices === 'none') { + return; + } + if (componentIndices !== 'all' && !zrUtil.isArray(componentIndices)) { + componentIndices = [componentIndices]; + } + + ecModel.eachComponent({mainType: componentName}, function (componentModel, index) { + if (componentIndices !== 'all' && zrUtil.indexOf(componentIndices, index) < 0) { + return; + } + + var grid; + var coordSys; + + (componentName === 'xAxis' || componentName === 'yAxis') + ? (grid = componentModel.axis.grid) + : (coordSys = componentModel.coordinateSystem); // geo + + var coordInfo; + + // Check duplicate and find cartesian when tranval to yAxis. + for (var i = 0, len = coordInfoList.length; i < len; i++) { + var cInfo = coordInfoList[i]; + if (true) { + zrUtil.assert( + cInfo[componentName + 'Index'] != index, + 'Coord should not be defined duplicately: ' + componentName + index + ); + } + // CoordSys is always required for `rect brush` or `polygon brush`. + // If both xAxisIndex and yAxisIndex specified, fetch cartesian by them. + if (componentName === 'yAxis' && !cInfo.yAxis && cInfo.xAxis) { + var aCoordSys = grid.getCartesian(cInfo.xAxisIndex, index); + if (aCoordSys) { // The yAxis and xAxis are in the same cartesian. + coordSys = aCoordSys; + coordInfo = cInfo; + break; + } + } + } + + !coordInfo && coordInfoList.push(coordInfo = {}); + + coordInfo[componentName] = componentModel; + coordInfo[componentName + 'Index'] = index; + // If both xAxisIndex and yAxisIndex specified, panelId only use yAxisIndex, + // which is enough to index panel. + coordInfo.panelId = componentName + PANEL_ID_SPLIT + index; + coordInfo.coordSys = coordSys + // If only xAxisIndex or only yAxisIndex specified, find its first cartesian. + || grid.getCartesian(coordInfo.xAxisIndex, coordInfo.yAxisIndex); + + coordInfo.coordSys + ? (coordInfoList[componentName + 'Has'] = true) + : coordInfoList.pop(); // If a coordInfo exists originally, existance of coordSys is ensured. + }); + }); + + return coordInfoList; + }; + + helper.controlSeries = function (area, brushModel, seriesModel) { + // Check whether area is bound in coord, and series do not belong to that coord. + // If do not do this check, some brush (like lineX) will controll all axes. + var coordInfo = findCoordInfo(area, brushModel.coordInfoList); + return coordInfo === true || (coordInfo && coordInfo.coordSys === seriesModel.coordinateSystem); + }; + + function formatMinMax(minMax) { + minMax[0] > minMax[1] && minMax.reverse(); + return minMax; + } + + /** + * If return Object, a coord found. + * If reutrn true, global found. + * Otherwise nothing found. + * + * @param {Object} area {Index} + * @param {Array} coordInfoList + * @return {Obejct|boolean} + */ + function findCoordInfo(area, coordInfoList) { + var isGlobal = true; + for (var j = 0; j < COMPONENT_NAMES.length; j++) { + var indexAttr = COMPONENT_NAMES[j] + 'Index'; + if (area[indexAttr] >= 0) { + isGlobal = false; + for (var i = 0; i < coordInfoList.length; i++) { + if (coordInfoList[i][indexAttr] === area[indexAttr]) { + return coordInfoList[i]; + } + } + } + } + return isGlobal; + } + + function axisConvert(axisName, to, coordInfo, coordRange) { + var axis = coordInfo.coordSys.getAxis(axisName); + + if (true) { + zrUtil.assert(axis, 'line brush is only available in cartesian (grid).'); + } + + return formatMinMax(zrUtil.map([0, 1], function (i) { + return to + ? axis.coordToData(axis.toLocalCoord(coordRange[i])) + : axis.toGlobalCoord(axis.dataToCoord(coordRange[i])); + })); + } + + var coordConvert = { + + lineX: zrUtil.curry(axisConvert, 'x'), + + lineY: zrUtil.curry(axisConvert, 'y'), + + rect: function (to, coordInfo, coordRange) { + var coordSys = coordInfo.coordSys; + var xminymin = coordSys[COORD_CONVERTS[to]]([coordRange[0][0], coordRange[1][0]]); + var xmaxymax = coordSys[COORD_CONVERTS[to]]([coordRange[0][1], coordRange[1][1]]); + return [ + formatMinMax([xminymin[0], xmaxymax[0]]), + formatMinMax([xminymin[1], xmaxymax[1]]) + ]; + }, + + polygon: function (to, coordInfo, coordRange) { + var coordSys = coordInfo.coordSys; + return zrUtil.map(coordRange, coordSys[COORD_CONVERTS[to]], coordSys); + } + }; + + module.exports = helper; + + + +/***/ }, +/* 310 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush model + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + var visualSolution = __webpack_require__(306); + var Model = __webpack_require__(12); + + var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd']; + + var BrushModel = echarts.extendComponentModel({ + + type: 'brush', + + dependencies: ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'], + + /** + * @protected + */ + defaultOption: { + // inBrush: null, + // outOfBrush: null, + toolbox: null, // Default value see preprocessor. + brushLink: null, // Series indices array, broadcast using dataIndex. + // or 'all', which means all series. 'none' or null means no series. + seriesIndex: 'all', // seriesIndex array, specify series controlled by this brush component. + geoIndex: null, // + xAxisIndex: null, + yAxisIndex: null, + + brushType: 'rect', // Default brushType, see BrushController. + brushMode: 'single', // Default brushMode, 'single' or 'multiple' + transformable: true, // Default transformable. + brushStyle: { // Default brushStyle + borderWidth: 1, + color: 'rgba(120,140,180,0.3)', + borderColor: 'rgba(120,140,180,0.8)', + width: null // do not use bursh width in line brush, but fetch from grid. + }, + + throttleType: 'fixRate',// Throttle in brushSelected event. 'fixRate' or 'debounce'. + // If null, no throttle. Valid only in the first brush component + throttleDelay: 0, // Unit: ms, 0 means every event will be triggered. + + // FIXME + // 试验效果 + removeOnClick: true + }, + + /** + * @readOnly + * @type {Array.} + */ + areas: [], + + /** + * Current activated brush type. + * If null, brush is inactived. + * see module:echarts/component/helper/BrushController + * @readOnly + * @type {string} + */ + brushType: null, + + /** + * Current brush opt. + * see module:echarts/component/helper/BrushController + * @readOnly + * @type {Object} + */ + brushOption: {}, + + /** + * @readOnly + * @type {Array.} + */ + coordInfoList: [], + + optionUpdated: function (newOption, isInit) { + var thisOption = this.option; + + !isInit && visualSolution.replaceVisualOption( + thisOption, newOption, ['inBrush', 'outOfBrush'] + ); + + thisOption.inBrush = thisOption.inBrush || {}; + // Always give default visual, consider setOption at the second time. + thisOption.outOfBrush = thisOption.outOfBrush || {color: DEFAULT_OUT_OF_BRUSH_COLOR}; + }, + + /** + * If ranges is null/undefined, range state remain. + * + * @param {Array.} [ranges] + */ + setAreas: function (areas) { + if (true) { + zrUtil.assert(zrUtil.isArray(areas)); + zrUtil.each(areas, function (area) { + zrUtil.assert(area.brushType, 'Illegal areas'); + }); + } + + // If ranges is null/undefined, range state remain. + // This helps user to dispatchAction({type: 'brush'}) with no areas + // set but just want to get the current brush select info from a `brush` event. + if (!areas) { + return; + } + + this.areas = zrUtil.map(areas, function (area) { + return this._mergeBrushOption(area); + }, this); + }, + + /** + * see module:echarts/component/helper/BrushController + * @param {Object} brushOption + */ + setBrushOption: function (brushOption) { + this.brushOption = this._mergeBrushOption(brushOption); + this.brushType = this.brushOption.brushType; + }, + + /** + * @private + */ + _mergeBrushOption: function (brushOption) { + var option = this.option; + return zrUtil.merge( + { + brushType: option.brushType, + brushMode: option.brushMode, + transformable: option.transformable, + brushStyle: new Model(option.brushStyle).getItemStyle(), + removeOnClick: option.removeOnClick + }, + brushOption, + true + ); + } + + }); + + module.exports = BrushModel; + + + +/***/ }, +/* 311 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var BrushController = __webpack_require__(233); + var echarts = __webpack_require__(1); + var brushHelper = __webpack_require__(309); + + module.exports = echarts.extendComponentView({ + + type: 'brush', + + init: function (ecModel, api) { + + /** + * @readOnly + * @type {module:echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @readOnly + * @type {module:echarts/ExtensionAPI} + */ + this.api = api; + + /** + * @readOnly + * @type {module:echarts/component/brush/BrushModel} + */ + this.model; + + /** + * @private + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)) + .mount(); + }, + + /** + * @override + */ + render: function (brushModel) { + this.model = brushModel; + return updateController.apply(this, arguments); + }, + + /** + * @override + */ + updateView: updateController, + + /** + * @override + */ + updateLayout: updateController, + + /** + * @override + */ + updateVisual: updateController, + + /** + * @override + */ + dispose: function () { + this._brushController.dispose(); + }, + + /** + * @private + */ + _onBrush: function (areas, opt) { + var modelId = this.model.id; + + brushHelper.parseOutputRanges(areas, this.model.coordInfoList, this.ecModel); + + // Action is not dispatched on drag end, because the drag end + // emits the same params with the last drag move event, and + // may have some delay when using touch pad, which makes + // animation not smooth (when using debounce). + (!opt.isEnd || opt.removeOnClick) && this.api.dispatchAction({ + type: 'brush', + brushId: modelId, + areas: zrUtil.clone(areas), + $from: modelId + }); + } + + }); + + function updateController(brushModel, ecModel, api, payload) { + // Do not update controller when drawing. + (!payload || payload.$from !== brushModel.id) && this._brushController + .setPanels(brushHelper.makePanelOpts(brushModel.coordInfoList)) + .enableBrush(brushModel.brushOption) + .updateCovers(brushModel.areas.slice()); + } + + + +/***/ }, +/* 312 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush action + */ + + + var echarts = __webpack_require__(1); + + /** + * payload: { + * brushIndex: number, or, + * brushId: string, or, + * brushName: string, + * globalRanges: Array + * } + */ + echarts.registerAction( + {type: 'brush', event: 'brush', update: 'updateView'}, + function (payload, ecModel) { + ecModel.eachComponent({mainType: 'brush', query: payload}, function (brushModel) { + brushModel.setAreas(payload.areas); + }); + } + ); + + /** + * payload: { + * brushComponents: [ + * { + * brushId, + * brushIndex, + * brushName, + * series: [ + * { + * seriesId, + * seriesIndex, + * seriesName, + * rawIndices: [21, 34, ...] + * }, + * ... + * ] + * }, + * ... + * ] + * } + */ + echarts.registerAction( + {type: 'brushSelect', event: 'brushSelected', update: 'none'}, + function () {} + ); + + +/***/ }, +/* 313 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + + function Brush(model, ecModel, api) { + this.model = model; + this.ecModel = ecModel; + this.api = api; + + /** + * @private + * @type {string} + */ + this._brushType; + + /** + * @private + * @type {string} + */ + this._brushMode; + } + + Brush.defaultOption = { + show: true, + type: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'], + icon: { + rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13', // jshint ignore:line + polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2', // jshint ignore:line + lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4', // jshint ignore:line + lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4', // jshint ignore:line + keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z', // jshint ignore:line + clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line + }, + title: { + rect: '矩形选择', + polygon: '圈选', + lineX: '横向选择', + lineY: '纵向选择', + keep: '保持选择', + clear: '清除选择' + } + }; + + var proto = Brush.prototype; + + proto.render = + proto.updateView = + proto.updateLayout = function (featureModel, ecModel, api) { + var brushType; + var brushMode; + var isBrushed; + + ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { + brushType = brushModel.brushType; + brushMode = brushModel.brushOption.brushMode || 'single'; + isBrushed |= brushModel.areas.length; + }); + this._brushType = brushType; + this._brushMode = brushMode; + + zrUtil.each(featureModel.get('type', true), function (type) { + featureModel.setIconStatus( + type, + ( + type === 'keep' + ? brushMode === 'multiple' + : type === 'clear' + ? isBrushed + : type === brushType + ) ? 'emphasis' : 'normal' + ); + }); + }; + + proto.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon', true); + var icons = {}; + zrUtil.each(model.get('type', true), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + + proto.onclick = function (ecModel, api, type) { + var api = this.api; + var brushType = this._brushType; + var brushMode = this._brushMode; + + if (type === 'clear') { + api.dispatchAction({ + type: 'brush', + // Clear all areas of all brush components. + areas: [] + }); + } + else { + api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'brush', + brushOption: { + brushType: type === 'keep' + ? brushType + : (brushType === type ? false : type), + brushMode: type === 'keep' + ? (brushMode === 'multiple' ? 'single' : 'multiple') + : brushMode + } + }); + } + }; + + featureManager.register('brush', Brush); + + module.exports = Brush; + + +/***/ }, +/* 314 */ +/***/ function(module, exports) { + + 'use strict'; + + + var features = {}; + + module.exports = { + register: function (name, ctor) { + features[name] = ctor; + }, + + get: function (name) { + return features[name]; + } + }; + + +/***/ }, +/* 315 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var echarts = __webpack_require__(1); + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + + // Model + echarts.extendComponentModel({ + + type: 'title', + + layoutMode: {type: 'box', ignoreSize: true}, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 6, + show: true, + + text: '', + // 超链接跳转 + // link: null, + // 仅支持self | blank + target: 'blank', + subtext: '', + + // 超链接跳转 + // sublink: null, + // 仅支持self | blank + subtarget: 'blank', + + // 'center' ¦ 'left' ¦ 'right' + // ¦ {number}(x坐标,单位px) + left: 0, + // 'top' ¦ 'bottom' ¦ 'center' + // ¦ {number}(y坐标,单位px) + top: 0, + + // 水平对齐 + // 'auto' | 'left' | 'right' | 'center' + // 默认根据 left 的位置判断是左对齐还是右对齐 + // textAlign: null + // + // 垂直对齐 + // 'auto' | 'top' | 'bottom' | 'middle' + // 默认根据 top 位置判断是上对齐还是下对齐 + // textBaseline: null + + backgroundColor: 'rgba(0,0,0,0)', + + // 标题边框颜色 + borderColor: '#ccc', + + // 标题边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + + // 标题内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + + // 主副标题纵向间隔,单位px,默认为10, + itemGap: 10, + textStyle: { + fontSize: 18, + fontWeight: 'bolder', + color: '#333' + }, + subtextStyle: { + color: '#aaa' + } + } + }); + + // View + echarts.extendComponentView({ + + type: 'title', + + render: function (titleModel, ecModel, api) { + this.group.removeAll(); + + if (!titleModel.get('show')) { + return; + } + + var group = this.group; + + var textStyleModel = titleModel.getModel('textStyle'); + var subtextStyleModel = titleModel.getModel('subtextStyle'); + + var textAlign = titleModel.get('textAlign'); + var textBaseline = titleModel.get('textBaseline'); + + var textEl = new graphic.Text({ + style: { + text: titleModel.get('text'), + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + }, + z2: 10 + }); + + var textRect = textEl.getBoundingRect(); + + var subText = titleModel.get('subtext'); + var subTextEl = new graphic.Text({ + style: { + text: subText, + textFont: subtextStyleModel.getFont(), + fill: subtextStyleModel.getTextColor(), + y: textRect.height + titleModel.get('itemGap'), + textBaseline: 'top' + }, + z2: 10 + }); + + var link = titleModel.get('link'); + var sublink = titleModel.get('sublink'); + + textEl.silent = !link; + subTextEl.silent = !sublink; + + if (link) { + textEl.on('click', function () { + window.open(link, '_' + titleModel.get('target')); + }); + } + if (sublink) { + subTextEl.on('click', function () { + window.open(sublink, '_' + titleModel.get('subtarget')); + }); + } + + group.add(textEl); + subText && group.add(subTextEl); + // If no subText, but add subTextEl, there will be an empty line. + + var groupRect = group.getBoundingRect(); + var layoutOption = titleModel.getBoxLayoutParams(); + layoutOption.width = groupRect.width; + layoutOption.height = groupRect.height; + var layoutRect = layout.getLayoutRect( + layoutOption, { + width: api.getWidth(), + height: api.getHeight() + }, titleModel.get('padding') + ); + // Adjust text align based on position + if (!textAlign) { + // Align left if title is on the left. center and right is same + textAlign = titleModel.get('left') || titleModel.get('right'); + if (textAlign === 'middle') { + textAlign = 'center'; + } + // Adjust layout by text align + if (textAlign === 'right') { + layoutRect.x += layoutRect.width; + } + else if (textAlign === 'center') { + layoutRect.x += layoutRect.width / 2; + } + } + if (!textBaseline) { + textBaseline = titleModel.get('top') || titleModel.get('bottom'); + if (textBaseline === 'center') { + textBaseline = 'middle'; + } + if (textBaseline === 'bottom') { + layoutRect.y += layoutRect.height; + } + else if (textBaseline === 'middle') { + layoutRect.y += layoutRect.height / 2; + } + + textBaseline = textBaseline || 'top'; + } + + group.attr('position', [layoutRect.x, layoutRect.y]); + var alignStyle = { + textAlign: textAlign, + textVerticalAlign: textBaseline + }; + textEl.setStyle(alignStyle); + subTextEl.setStyle(alignStyle); + + // Render background + // Get groupRect again because textAlign has been changed + groupRect = group.getBoundingRect(); + var padding = layoutRect.margin; + var style = titleModel.getItemStyle(['color', 'opacity']); + style.fill = titleModel.get('backgroundColor'); + var rect = new graphic.Rect({ + shape: { + x: groupRect.x - padding[3], + y: groupRect.y - padding[0], + width: groupRect.width + padding[1] + padding[3], + height: groupRect.height + padding[0] + padding[2] + }, + style: style, + silent: true + }); + graphic.subPixelOptimizeRect(rect); + + group.add(rect); + } + }); + + +/***/ }, +/* 316 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(317); + + __webpack_require__(318); + __webpack_require__(321); + + __webpack_require__(322); + __webpack_require__(323); + + __webpack_require__(325); + __webpack_require__(326); + + __webpack_require__(328); + __webpack_require__(329); + + + +/***/ }, +/* 317 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('dataZoom', function (option) { + // Default 'slider' when no type specified. + return 'slider'; + }); + + + +/***/ }, +/* 318 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + var echarts = __webpack_require__(1); + var modelUtil = __webpack_require__(5); + var helper = __webpack_require__(319); + var AxisProxy = __webpack_require__(320); + var each = zrUtil.each; + var eachAxisDim = helper.eachAxisDim; + + var DataZoomModel = echarts.extendComponentModel({ + + type: 'dataZoom', + + dependencies: [ + 'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'series' + ], + + /** + * @protected + */ + defaultOption: { + zlevel: 0, + z: 4, // Higher than normal component (z: 2). + orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'. + xAxisIndex: null, // Default the first horizontal category axis. + yAxisIndex: null, // Default the first vertical category axis. + + + filterMode: 'filter', // Possible values: 'filter' or 'empty'. + // 'filter': data items which are out of window will be removed. + // This option is applicable when filtering outliers. + // 'empty': data items which are out of window will be set to empty. + // This option is applicable when user should not neglect + // that there are some data items out of window. + // Taking line chart as an example, line will be broken in + // the filtered points when filterModel is set to 'empty', but + // be connected when set to 'filter'. + + throttle: null, // Dispatch action by the fixed rate, avoid frequency. + // default 100. Do not throttle when use null/undefined. + // If animation === true and animationDurationUpdate > 0, + // default value is 100, otherwise 20. + start: 0, // Start percent. 0 ~ 100 + end: 100, // End percent. 0 ~ 100 + startValue: null, // Start value. If startValue specified, start is ignored. + endValue: null // End value. If endValue specified, end is ignored. + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel) { + + /** + * key like x_0, y_1 + * @private + * @type {Object} + */ + this._dataIntervalByAxis = {}; + + /** + * @private + */ + this._dataInfo = {}; + + /** + * key like x_0, y_1 + * @private + */ + this._axisProxies = {}; + + /** + * @readOnly + */ + this.textStyleModel; + + /** + * @private + */ + this._autoThrottle = true; + + var rawOption = retrieveRaw(option); + + this.mergeDefaultAndTheme(option, ecModel); + + this.doInit(rawOption); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var rawOption = retrieveRaw(newOption); + + //FIX #2591 + zrUtil.merge(this.option, newOption, true); + + this.doInit(rawOption); + }, + + /** + * @protected + */ + doInit: function (rawOption) { + var thisOption = this.option; + + // Disable realtime view update if canvas is not supported. + if (!env.canvasSupported) { + thisOption.realtime = false; + } + + this._setDefaultThrottle(rawOption); + + processRangeProp('start', 'startValue', rawOption, thisOption); + processRangeProp('end', 'endValue', rawOption, thisOption); + + this.textStyleModel = this.getModel('textStyle'); + + this._resetTarget(); + + this._giveAxisProxies(); + }, + + /** + * @private + */ + _giveAxisProxies: function () { + var axisProxies = this._axisProxies; + + this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) { + var axisModel = this.dependentModels[dimNames.axis][axisIndex]; + + // If exists, share axisProxy with other dataZoomModels. + var axisProxy = axisModel.__dzAxisProxy || ( + // Use the first dataZoomModel as the main model of axisProxy. + axisModel.__dzAxisProxy = new AxisProxy( + dimNames.name, axisIndex, this, ecModel + ) + ); + // FIXME + // dispose __dzAxisProxy + + axisProxies[dimNames.name + '_' + axisIndex] = axisProxy; + }, this); + }, + + /** + * @private + */ + _resetTarget: function () { + var thisOption = this.option; + + var autoMode = this._judgeAutoMode(); + + eachAxisDim(function (dimNames) { + var axisIndexName = dimNames.axisIndex; + thisOption[axisIndexName] = modelUtil.normalizeToArray( + thisOption[axisIndexName] + ); + }, this); + + if (autoMode === 'axisIndex') { + this._autoSetAxisIndex(); + } + else if (autoMode === 'orient') { + this._autoSetOrient(); + } + }, + + /** + * @private + */ + _judgeAutoMode: function () { + // Auto set only works for setOption at the first time. + // The following is user's reponsibility. So using merged + // option is OK. + var thisOption = this.option; + + var hasIndexSpecified = false; + eachAxisDim(function (dimNames) { + // When user set axisIndex as a empty array, we think that user specify axisIndex + // but do not want use auto mode. Because empty array may be encountered when + // some error occured. + if (thisOption[dimNames.axisIndex] != null) { + hasIndexSpecified = true; + } + }, this); + + var orient = thisOption.orient; + + if (orient == null && hasIndexSpecified) { + return 'orient'; + } + else if (!hasIndexSpecified) { + if (orient == null) { + thisOption.orient = 'horizontal'; + } + return 'axisIndex'; + } + }, + + /** + * @private + */ + _autoSetAxisIndex: function () { + var autoAxisIndex = true; + var orient = this.get('orient', true); + var thisOption = this.option; + + if (autoAxisIndex) { + // Find axis that parallel to dataZoom as default. + var dimNames = orient === 'vertical' + ? {dim: 'y', axisIndex: 'yAxisIndex', axis: 'yAxis'} + : {dim: 'x', axisIndex: 'xAxisIndex', axis: 'xAxis'}; + + if (this.dependentModels[dimNames.axis].length) { + thisOption[dimNames.axisIndex] = [0]; + autoAxisIndex = false; + } + } + + if (autoAxisIndex) { + // Find the first category axis as default. (consider polar) + eachAxisDim(function (dimNames) { + if (!autoAxisIndex) { + return; + } + var axisIndices = []; + var axisModels = this.dependentModels[dimNames.axis]; + if (axisModels.length && !axisIndices.length) { + for (var i = 0, len = axisModels.length; i < len; i++) { + if (axisModels[i].get('type') === 'category') { + axisIndices.push(i); + } + } + } + thisOption[dimNames.axisIndex] = axisIndices; + if (axisIndices.length) { + autoAxisIndex = false; + } + }, this); + } + + if (autoAxisIndex) { + // FIXME + // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制), + // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)? + + // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified, + // dataZoom component auto adopts series that reference to + // both xAxis and yAxis which type is 'value'. + this.ecModel.eachSeries(function (seriesModel) { + if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) { + eachAxisDim(function (dimNames) { + var axisIndices = thisOption[dimNames.axisIndex]; + + var axisIndex = seriesModel.get(dimNames.axisIndex); + var axisId = seriesModel.get(dimNames.axisId); + + var axisModel = seriesModel.ecModel.queryComponents({ + mainType: dimNames.axis, + index: axisIndex, + id: axisId + })[0]; + + if (true) { + if (!axisModel) { + throw new Error( + dimNames.axis + ' "' + zrUtil.retrieve( + axisIndex, + axisId, + 0 + ) + '" not found' + ); + } + } + axisIndex = axisModel.componentIndex; + + if (zrUtil.indexOf(axisIndices, axisIndex) < 0) { + axisIndices.push(axisIndex); + } + }); + } + }, this); + } + }, + + /** + * @private + */ + _autoSetOrient: function () { + var dim; + + // Find the first axis + this.eachTargetAxis(function (dimNames) { + !dim && (dim = dimNames.name); + }, this); + + this.option.orient = dim === 'y' ? 'vertical' : 'horizontal'; + }, + + /** + * @private + */ + _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) { + // FIXME + // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。 + // 例如series.type === scatter时。 + + var is = true; + eachAxisDim(function (dimNames) { + var seriesAxisIndex = seriesModel.get(dimNames.axisIndex); + var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex]; + + if (!axisModel || axisModel.get('type') !== axisType) { + is = false; + } + }, this); + return is; + }, + + /** + * @private + */ + _setDefaultThrottle: function (rawOption) { + // When first time user set throttle, auto throttle ends. + if (rawOption.hasOwnProperty('throttle')) { + this._autoThrottle = false; + } + if (this._autoThrottle) { + var globalOption = this.ecModel.option; + this.option.throttle = + (globalOption.animation && globalOption.animationDurationUpdate > 0) + ? 100 : 20; + } + }, + + /** + * @public + */ + getFirstTargetAxisModel: function () { + var firstAxisModel; + eachAxisDim(function (dimNames) { + if (firstAxisModel == null) { + var indices = this.get(dimNames.axisIndex); + if (indices.length) { + firstAxisModel = this.dependentModels[dimNames.axis][indices[0]]; + } + } + }, this); + + return firstAxisModel; + }, + + /** + * @public + * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel + */ + eachTargetAxis: function (callback, context) { + var ecModel = this.ecModel; + eachAxisDim(function (dimNames) { + each( + this.get(dimNames.axisIndex), + function (axisIndex) { + callback.call(context, dimNames, axisIndex, this, ecModel); + }, + this + ); + }, this); + }, + + getAxisProxy: function (dimName, axisIndex) { + return this._axisProxies[dimName + '_' + axisIndex]; + }, + + /** + * If not specified, set to undefined. + * + * @public + * @param {Object} opt + * @param {number} [opt.start] + * @param {number} [opt.end] + * @param {number} [opt.startValue] + * @param {number} [opt.endValue] + */ + setRawRange: function (opt) { + each(['start', 'end', 'startValue', 'endValue'], function (name) { + // If any of those prop is null/undefined, we should alos set + // them, because only one pair between start/end and + // startValue/endValue can work. + this.option[name] = opt[name]; + }, this); + }, + + /** + * @public + * @return {Array.} [startPercent, endPercent] + */ + getPercentRange: function () { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataPercentWindow(); + } + }, + + /** + * @public + * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0); + * + * @param {string} [axisDimName] + * @param {number} [axisIndex] + * @return {Array.} [startValue, endValue] + */ + getValueRange: function (axisDimName, axisIndex) { + if (axisDimName == null && axisIndex == null) { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataValueWindow(); + } + } + else { + return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow(); + } + }, + + /** + * @public + * @return {module:echarts/component/dataZoom/AxisProxy} + */ + findRepresentativeAxisProxy: function () { + // Find the first hosted axisProxy + var axisProxies = this._axisProxies; + for (var key in axisProxies) { + if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) { + return axisProxies[key]; + } + } + + // If no hosted axis find not hosted axisProxy. + // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis, + // and the option.start or option.end settings are different. The percentRange + // should follow axisProxy. + // (We encounter this problem in toolbox data zoom.) + for (var key in axisProxies) { + if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) { + return axisProxies[key]; + } + } + } + + }); + + function retrieveRaw(option) { + var ret = {}; + each( + ['start', 'end', 'startValue', 'endValue', 'throttle'], + function (name) { + option.hasOwnProperty(name) && (ret[name] = option[name]); + } + ); + return ret; + } + + function processRangeProp(percentProp, valueProp, rawOption, thisOption) { + // start/end has higher priority over startValue/endValue, + // but we should make chart.setOption({endValue: 1000}) effective, + // rather than chart.setOption({endValue: 1000, end: null}). + if (rawOption[valueProp] != null && rawOption[percentProp] == null) { + thisOption[percentProp] = null; + } + // Otherwise do nothing and use the merge result. + } + + module.exports = DataZoomModel; + + + +/***/ }, +/* 319 */ +/***/ function(module, exports, __webpack_require__) { + + + var formatUtil = __webpack_require__(6); + var zrUtil = __webpack_require__(4); + + var helper = {}; + + var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle']; + + /** + * Create "each" method to iterate names. + * + * @pubilc + * @param {Array.} names + * @param {Array.=} attrs + * @return {Function} + */ + helper.createNameEach = function (names, attrs) { + names = names.slice(); + var capitalNames = zrUtil.map(names, formatUtil.capitalFirst); + attrs = (attrs || []).slice(); + var capitalAttrs = zrUtil.map(attrs, formatUtil.capitalFirst); + + return function (callback, context) { + zrUtil.each(names, function (name, index) { + var nameObj = {name: name, capital: capitalNames[index]}; + + for (var j = 0; j < attrs.length; j++) { + nameObj[attrs[j]] = name + capitalAttrs[j]; + } + + callback.call(context, nameObj); + }); + }; + }; + + /** + * Iterate each dimension name. + * + * @public + * @param {Function} callback The parameter is like: + * { + * name: 'angle', + * capital: 'Angle', + * axis: 'angleAxis', + * axisIndex: 'angleAixs', + * index: 'angleIndex' + * } + * @param {Object} context + */ + helper.eachAxisDim = helper.createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']); + + /** + * If tow dataZoomModels has the same axis controlled, we say that they are 'linked'. + * dataZoomModels and 'links' make up one or more graphics. + * This function finds the graphic where the source dataZoomModel is in. + * + * @public + * @param {Function} forEachNode Node iterator. + * @param {Function} forEachEdgeType edgeType iterator + * @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id. + * @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}} + */ + helper.createLinkedNodesFinder = function (forEachNode, forEachEdgeType, edgeIdGetter) { + + return function (sourceNode) { + var result = { + nodes: [], + records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean). + }; + + forEachEdgeType(function (edgeType) { + result.records[edgeType.name] = {}; + }); + + if (!sourceNode) { + return result; + } + + absorb(sourceNode, result); + + var existsLink; + do { + existsLink = false; + forEachNode(processSingleNode); + } + while (existsLink); + + function processSingleNode(node) { + if (!isNodeAbsorded(node, result) && isLinked(node, result)) { + absorb(node, result); + existsLink = true; + } + } + + return result; + }; + + function isNodeAbsorded(node, result) { + return zrUtil.indexOf(result.nodes, node) >= 0; + } + + function isLinked(node, result) { + var hasLink = false; + forEachEdgeType(function (edgeType) { + zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) { + result.records[edgeType.name][edgeId] && (hasLink = true); + }); + }); + return hasLink; + } + + function absorb(node, result) { + result.nodes.push(node); + forEachEdgeType(function (edgeType) { + zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) { + result.records[edgeType.name][edgeId] = true; + }); + }); + } + }; + + module.exports = helper; + + +/***/ }, +/* 320 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Axis operator + */ + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var each = zrUtil.each; + var asc = numberUtil.asc; + + /** + * Operate single axis. + * One axis can only operated by one axis operator. + * Different dataZoomModels may be defined to operate the same axis. + * (i.e. 'inside' data zoom and 'slider' data zoom components) + * So dataZoomModels share one axisProxy in that case. + * + * @class + */ + var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) { + + /** + * @private + * @type {string} + */ + this._dimName = dimName; + + /** + * @private + */ + this._axisIndex = axisIndex; + + /** + * @private + * @type {Array.} + */ + this._valueWindow; + + /** + * @private + * @type {Array.} + */ + this._percentWindow; + + /** + * @private + * @type {Array.} + */ + this._dataExtent; + + /** + * @readOnly + * @type {module: echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @private + * @type {module: echarts/component/dataZoom/DataZoomModel} + */ + this._dataZoomModel = dataZoomModel; + }; + + AxisProxy.prototype = { + + constructor: AxisProxy, + + /** + * Whether the axisProxy is hosted by dataZoomModel. + * + * @public + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + * @return {boolean} + */ + hostedBy: function (dataZoomModel) { + return this._dataZoomModel === dataZoomModel; + }, + + /** + * @return {Array.} + */ + getDataExtent: function () { + return this._dataExtent.slice(); + }, + + /** + * @return {Array.} + */ + getDataValueWindow: function () { + return this._valueWindow.slice(); + }, + + /** + * @return {Array.} + */ + getDataPercentWindow: function () { + return this._percentWindow.slice(); + }, + + /** + * @public + * @param {number} axisIndex + * @return {Array} seriesModels + */ + getTargetSeriesModels: function () { + var seriesModels = []; + var ecModel = this.ecModel; + + ecModel.eachSeries(function (seriesModel) { + var dimName = this._dimName; + var axisModel = ecModel.queryComponents({ + mainType: dimName + 'Axis', + index: seriesModel.get(dimName + 'AxisIndex'), + id: seriesModel.get(dimName + 'AxisId') + })[0]; + if (this._axisIndex === (axisModel && axisModel.componentIndex)) { + seriesModels.push(seriesModel); + } + }, this); + + return seriesModels; + }, + + getAxisModel: function () { + return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex); + }, + + getOtherAxisModel: function () { + var axisDim = this._dimName; + var ecModel = this.ecModel; + var axisModel = this.getAxisModel(); + var isCartesian = axisDim === 'x' || axisDim === 'y'; + var otherAxisDim; + var coordSysIndexName; + if (isCartesian) { + coordSysIndexName = 'gridIndex'; + otherAxisDim = axisDim === 'x' ? 'y' : 'x'; + } + else { + coordSysIndexName = 'polarIndex'; + otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle'; + } + var foundOtherAxisModel; + ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) { + if ((otherAxisModel.get(coordSysIndexName) || 0) + === (axisModel.get(coordSysIndexName) || 0) + ) { + foundOtherAxisModel = otherAxisModel; + } + }); + return foundOtherAxisModel; + }, + + /** + * Notice: reset should not be called before series.restoreData() called, + * so it is recommanded to be called in "process stage" but not "model init + * stage". + * + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + reset: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + // Culculate data window and data extent, and record them. + var dataExtent = this._dataExtent = calculateDataExtent( + this._dimName, this.getTargetSeriesModels() + ); + var dataWindow = calculateDataWindow( + dataZoomModel.option, dataExtent, this + ); + this._valueWindow = dataWindow.valueWindow; + this._percentWindow = dataWindow.percentWindow; + + // Update axis setting then. + setAxisModel(this); + }, + + /** + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + restore: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + this._valueWindow = this._percentWindow = null; + setAxisModel(this, true); + }, + + /** + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + filterData: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + var axisDim = this._dimName; + var seriesModels = this.getTargetSeriesModels(); + var filterMode = dataZoomModel.get('filterMode'); + var valueWindow = this._valueWindow; + + // FIXME + // Toolbox may has dataZoom injected. And if there are stacked bar chart + // with NaN data, NaN will be filtered and stack will be wrong. + // So we need to force the mode to be set empty. + // In fect, it is not a big deal that do not support filterMode-'filter' + // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis + // selection" some day, which might need "adapt to data extent on the + // otherAxis", which is disabled by filterMode-'empty'. + var otherAxisModel = this.getOtherAxisModel(); + if (dataZoomModel.get('$fromToolbox') + && otherAxisModel + && otherAxisModel.get('type') === 'category' + ) { + filterMode = 'empty'; + } + + // Process series data + each(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + + seriesData && each(seriesModel.coordDimToDataDim(axisDim), function (dim) { + if (filterMode === 'empty') { + seriesModel.setData( + seriesData.map(dim, function (value) { + return !isInWindow(value) ? NaN : value; + }) + ); + } + else { + seriesData.filterSelf(dim, isInWindow); + } + }); + }); + + function isInWindow(value) { + return value >= valueWindow[0] && value <= valueWindow[1]; + } + } + }; + + function calculateDataExtent(axisDim, seriesModels) { + var dataExtent = [Infinity, -Infinity]; + + each(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + if (seriesData) { + each(seriesModel.coordDimToDataDim(axisDim), function (dim) { + var seriesExtent = seriesData.getDataExtent(dim); + seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]); + seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]); + }); + } + }, this); + + return dataExtent; + } + + function calculateDataWindow(opt, dataExtent, axisProxy) { + var axisModel = axisProxy.getAxisModel(); + var scale = axisModel.axis.scale; + var percentExtent = [0, 100]; + var percentWindow = [ + opt.start, + opt.end + ]; + var valueWindow = []; + + // In percent range is used and axis min/max/scale is set, + // window should be based on min/max/0, but should not be + // based on the extent of filtered data. + dataExtent = dataExtent.slice(); + fixExtendByAxis(dataExtent, axisModel, scale); + + each(['startValue', 'endValue'], function (prop) { + valueWindow.push( + opt[prop] != null + ? scale.parse(opt[prop]) + : null + ); + }); + + // Normalize bound. + each([0, 1], function (idx) { + var boundValue = valueWindow[idx]; + var boundPercent = percentWindow[idx]; + + // start/end has higher priority over startValue/endValue, + // because start/end can be consistent among different type + // of axis but startValue/endValue not. + + if (boundPercent != null || boundValue == null) { + if (boundPercent == null) { + boundPercent = percentExtent[idx]; + } + // Use scale.parse to math round for category or time axis. + boundValue = scale.parse(numberUtil.linearMap( + boundPercent, percentExtent, dataExtent, true + )); + } + else { // boundPercent == null && boundValue != null + boundPercent = numberUtil.linearMap( + boundValue, dataExtent, percentExtent, true + ); + } + // valueWindow[idx] = round(boundValue); + // percentWindow[idx] = round(boundPercent); + valueWindow[idx] = boundValue; + percentWindow[idx] = boundPercent; + }); + + return { + valueWindow: asc(valueWindow), + percentWindow: asc(percentWindow) + }; + } + + function fixExtendByAxis(dataExtent, axisModel, scale) { + each(['min', 'max'], function (minMax, index) { + var axisMax = axisModel.get(minMax, true); + // Consider 'dataMin', 'dataMax' + if (axisMax != null && (axisMax + '').toLowerCase() !== 'data' + minMax) { + dataExtent[index] = scale.parse(axisMax); + } + }); + + if (!axisModel.get('scale', true)) { + dataExtent[0] > 0 && (dataExtent[0] = 0); + dataExtent[1] < 0 && (dataExtent[1] = 0); + } + + return dataExtent; + } + + function setAxisModel(axisProxy, isRestore) { + var axisModel = axisProxy.getAxisModel(); + + var percentWindow = axisProxy._percentWindow; + var valueWindow = axisProxy._valueWindow; + + if (!percentWindow) { + return; + } + + var isFull = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100); + // [0, 500]: arbitrary value, guess axis extent. + var precision = !isRestore && numberUtil.getPixelPrecision(valueWindow, [0, 500]); + // toFixed() digits argument must be between 0 and 20 + var invalidPrecision = !isRestore && !(precision < 20 && precision >= 0); + + var useOrigin = isRestore || isFull || invalidPrecision; + + axisModel.setRange && axisModel.setRange( + useOrigin ? null : +valueWindow[0].toFixed(precision), + useOrigin ? null : +valueWindow[1].toFixed(precision) + ); + } + + module.exports = AxisProxy; + + + +/***/ }, +/* 321 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentView = __webpack_require__(29); + + module.exports = ComponentView.extend({ + + type: 'dataZoom', + + render: function (dataZoomModel, ecModel, api, payload) { + this.dataZoomModel = dataZoomModel; + this.ecModel = ecModel; + this.api = api; + }, + + /** + * Find the first target coordinate system. + * + * @protected + * @return {Object} { + * cartesians: [ + * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1}, + * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0}, + * ... + * ], // cartesians must not be null/undefined. + * polars: [ + * {model: coord0, axisModels: [axis4], coordIndex: 0}, + * ... + * ], // polars must not be null/undefined. + * axisModels: [axis0, axis1, axis2, axis3, axis4] + * // axisModels must not be null/undefined. + * } + */ + getTargetInfo: function () { + var dataZoomModel = this.dataZoomModel; + var ecModel = this.ecModel; + var cartesians = []; + var polars = []; + var axisModels = []; + + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + var axisModel = ecModel.getComponent(dimNames.axis, axisIndex); + if (axisModel) { + axisModels.push(axisModel); + var coordSysName; + if (dimNames.axis === 'xAxis' || dimNames.axis === 'yAxis') { + coordSysName = 'grid'; + } + else { + // Polar + coordSysName = 'polar'; + } + var coordModel = ecModel.queryComponents({ + mainType: coordSysName, + index: axisModel.get(coordSysName + 'Index'), + id: axisModel.get(coordSysName + 'Id') + })[0]; + + if (coordModel != null) { + save(coordModel, axisModel, coordSysName === 'grid' ? cartesians : polars, coordModel.componentIndex); + } + } + }, this); + + function save(coordModel, axisModel, store, coordIndex) { + var item; + for (var i = 0; i < store.length; i++) { + if (store[i].model === coordModel) { + item = store[i]; + break; + } + } + if (!item) { + store.push(item = { + model: coordModel, axisModels: [], coordIndex: coordIndex + }); + } + item.axisModels.push(axisModel); + } + + return { + cartesians: cartesians, + polars: polars, + axisModels: axisModels + }; + } + + }); + + + +/***/ }, +/* 322 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var DataZoomModel = __webpack_require__(318); + + var SliderZoomModel = DataZoomModel.extend({ + + type: 'dataZoom.slider', + + layoutMode: 'box', + + /** + * @protected + */ + defaultOption: { + show: true, + + // ph => placeholder. Using placehoder here because + // deault value can only be drived in view stage. + right: 'ph', // Default align to grid rect. + top: 'ph', // Default align to grid rect. + width: 'ph', // Default align to grid rect. + height: 'ph', // Default align to grid rect. + left: null, // Default align to grid rect. + bottom: null, // Default align to grid rect. + + backgroundColor: 'rgba(47,69,84,0)', // Background of slider zoom component. + // dataBackgroundColor: '#ddd', // Background coor of data shadow and border of box, + // highest priority, remain for compatibility of + // previous version, but not recommended any more. + dataBackground: { + lineStyle: { + color: '#2f4554', + width: 0.5, + opacity: 0.3 + }, + areaStyle: { + color: 'rgba(47,69,84,0.3)', + opacity: 0.3 + } + }, + borderColor: '#ddd', // border color of the box. For compatibility, + // if dataBackgroundColor is set, borderColor + // is ignored. + + fillerColor: 'rgba(167,183,204,0.4)', // Color of selected area. + // handleColor: 'rgba(89,170,216,0.95)', // Color of handle. + // handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.6,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z', + handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z', + // Percent of the slider height + handleSize: '100%', + + handleStyle: { + color: '#a7b7cc' + }, + + labelPrecision: null, + labelFormatter: null, + showDetail: true, + showDataShadow: 'auto', // Default auto decision. + realtime: true, + zoomLock: false, // Whether disable zoom. + textStyle: { + color: '#333' + } + } + + }); + + module.exports = SliderZoomModel; + + + +/***/ }, +/* 323 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var throttle = __webpack_require__(308); + var DataZoomView = __webpack_require__(321); + var Rect = graphic.Rect; + var numberUtil = __webpack_require__(7); + var linearMap = numberUtil.linearMap; + var layout = __webpack_require__(21); + var sliderMove = __webpack_require__(324); + var asc = numberUtil.asc; + var bind = zrUtil.bind; + // var mathMax = Math.max; + var each = zrUtil.each; + + // Constants + var DEFAULT_LOCATION_EDGE_GAP = 7; + var DEFAULT_FRAME_BORDER_WIDTH = 1; + var DEFAULT_FILLER_SIZE = 30; + var HORIZONTAL = 'horizontal'; + var VERTICAL = 'vertical'; + var LABEL_GAP = 5; + var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; + + var SliderZoomView = DataZoomView.extend({ + + type: 'dataZoom.slider', + + init: function (ecModel, api) { + + /** + * @private + * @type {Object} + */ + this._displayables = {}; + + /** + * @private + * @type {string} + */ + this._orient; + + /** + * [0, 100] + * @private + */ + this._range; + + /** + * [coord of the first handle, coord of the second handle] + * @private + */ + this._handleEnds; + + /** + * [length, thick] + * @private + * @type {Array.} + */ + this._size; + + /** + * @private + * @type {number} + */ + this._handleWidth; + + /** + * @private + * @type {number} + */ + this._handleHeight; + + /** + * @private + */ + this._location; + + /** + * @private + */ + this._dragging; + + /** + * @private + */ + this._dataShadowInfo; + + this.api = api; + }, + + /** + * @override + */ + render: function (dataZoomModel, ecModel, api, payload) { + SliderZoomView.superApply(this, 'render', arguments); + + throttle.createOrUpdate( + this, + '_dispatchZoomAction', + this.dataZoomModel.get('throttle'), + 'fixRate' + ); + + this._orient = dataZoomModel.get('orient'); + + if (this.dataZoomModel.get('show') === false) { + this.group.removeAll(); + return; + } + + // Notice: this._resetInterval() should not be executed when payload.type + // is 'dataZoom', origin this._range should be maintained, otherwise 'pan' + // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction, + if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) { + this._buildView(); + } + + this._updateView(); + }, + + /** + * @override + */ + remove: function () { + SliderZoomView.superApply(this, 'remove', arguments); + throttle.clear(this, '_dispatchZoomAction'); + }, + + /** + * @override + */ + dispose: function () { + SliderZoomView.superApply(this, 'dispose', arguments); + throttle.clear(this, '_dispatchZoomAction'); + }, + + _buildView: function () { + var thisGroup = this.group; + + thisGroup.removeAll(); + + this._resetLocation(); + this._resetInterval(); + + var barGroup = this._displayables.barGroup = new graphic.Group(); + + this._renderBackground(); + + this._renderHandle(); + + this._renderDataShadow(); + + thisGroup.add(barGroup); + + this._positionGroup(); + }, + + /** + * @private + */ + _resetLocation: function () { + var dataZoomModel = this.dataZoomModel; + var api = this.api; + + // If some of x/y/width/height are not specified, + // auto-adapt according to target grid. + var coordRect = this._findCoordRect(); + var ecSize = {width: api.getWidth(), height: api.getHeight()}; + // Default align by coordinate system rect. + var positionInfo = this._orient === HORIZONTAL + ? { + // Why using 'right', because right should be used in vertical, + // and it is better to be consistent for dealing with position param merge. + right: ecSize.width - coordRect.x - coordRect.width, + top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP), + width: coordRect.width, + height: DEFAULT_FILLER_SIZE + } + : { // vertical + right: DEFAULT_LOCATION_EDGE_GAP, + top: coordRect.y, + width: DEFAULT_FILLER_SIZE, + height: coordRect.height + }; + + // Do not write back to option and replace value 'ph', because + // the 'ph' value should be recalculated when resize. + var layoutParams = layout.getLayoutParams(dataZoomModel.option); + + // Replace the placeholder value. + zrUtil.each(['right', 'top', 'width', 'height'], function (name) { + if (layoutParams[name] === 'ph') { + layoutParams[name] = positionInfo[name]; + } + }); + + var layoutRect = layout.getLayoutRect( + layoutParams, + ecSize, + dataZoomModel.padding + ); + + this._location = {x: layoutRect.x, y: layoutRect.y}; + this._size = [layoutRect.width, layoutRect.height]; + this._orient === VERTICAL && this._size.reverse(); + }, + + /** + * @private + */ + _positionGroup: function () { + var thisGroup = this.group; + var location = this._location; + var orient = this._orient; + + // Just use the first axis to determine mapping. + var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel(); + var inverse = targetAxisModel && targetAxisModel.get('inverse'); + + var barGroup = this._displayables.barGroup; + var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; + + // Transform barGroup. + barGroup.attr( + (orient === HORIZONTAL && !inverse) + ? {scale: otherAxisInverse ? [1, 1] : [1, -1]} + : (orient === HORIZONTAL && inverse) + ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]} + : (orient === VERTICAL && !inverse) + ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2} + // Dont use Math.PI, considering shadow direction. + : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2} + ); + + // Position barGroup + var rect = thisGroup.getBoundingRect([barGroup]); + thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]); + }, + + /** + * @private + */ + _getViewExtent: function () { + return [0, this._size[0]]; + }, + + _renderBackground : function () { + var dataZoomModel = this.dataZoomModel; + var size = this._size; + + this._displayables.barGroup.add(new Rect({ + silent: true, + shape: { + x: 0, y: 0, width: size[0], height: size[1] + }, + style: { + fill: dataZoomModel.get('backgroundColor') + }, + z2: -40 + })); + }, + + _renderDataShadow: function () { + var info = this._dataShadowInfo = this._prepareDataShadowInfo(); + + if (!info) { + return; + } + + var size = this._size; + var seriesModel = info.series; + var data = seriesModel.getRawData(); + var otherDim = seriesModel.getShadowDim + ? seriesModel.getShadowDim() // @see candlestick + : info.otherDim; + + var otherDataExtent = data.getDataExtent(otherDim); + // Nice extent. + var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3; + otherDataExtent = [ + otherDataExtent[0] - otherOffset, + otherDataExtent[1] + otherOffset + ]; + var otherShadowExtent = [0, size[1]]; + + var thisShadowExtent = [0, size[0]]; + + var areaPoints = [[size[0], 0], [0, 0]]; + var linePoints = []; + var step = thisShadowExtent[1] / (data.count() - 1); + var thisCoord = 0; + + // Optimize for large data shadow + var stride = Math.round(data.count() / size[0]); + data.each([otherDim], function (value, index) { + if (stride > 0 && (index % stride)) { + thisCoord += step; + return; + } + // FIXME + // 应该使用统计的空判断?还是在list里进行空判断? + var otherCoord = (value == null || isNaN(value) || value === '') + ? null + : linearMap(value, otherDataExtent, otherShadowExtent, true); + if (otherCoord != null) { + areaPoints.push([thisCoord, otherCoord]); + linePoints.push([thisCoord, otherCoord]); + } + + thisCoord += step; + }); + + var dataZoomModel = this.dataZoomModel; + // var dataBackgroundModel = dataZoomModel.getModel('dataBackground'); + this._displayables.barGroup.add(new graphic.Polygon({ + shape: {points: areaPoints}, + style: zrUtil.defaults( + {fill: dataZoomModel.get('dataBackgroundColor')}, + dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle() + ), + silent: true, + z2: -20 + })); + this._displayables.barGroup.add(new graphic.Polyline({ + shape: {points: linePoints}, + style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(), + silent: true, + z2: -19 + })); + }, + + _prepareDataShadowInfo: function () { + var dataZoomModel = this.dataZoomModel; + var showDataShadow = dataZoomModel.get('showDataShadow'); + + if (showDataShadow === false) { + return; + } + + // Find a representative series. + var result; + var ecModel = this.ecModel; + + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + var seriesModels = dataZoomModel + .getAxisProxy(dimNames.name, axisIndex) + .getTargetSeriesModels(); + + zrUtil.each(seriesModels, function (seriesModel) { + if (result) { + return; + } + + if (showDataShadow !== true && zrUtil.indexOf( + SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type') + ) < 0 + ) { + return; + } + + var otherDim = getOtherDim(dimNames.name); + + var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis; + + result = { + thisAxis: thisAxis, + series: seriesModel, + thisDim: dimNames.name, + otherDim: otherDim, + otherAxisInverse: seriesModel + .coordinateSystem.getOtherAxis(thisAxis).inverse + }; + + }, this); + + }, this); + + return result; + }, + + _renderHandle: function () { + var displaybles = this._displayables; + var handles = displaybles.handles = []; + var handleLabels = displaybles.handleLabels = []; + var barGroup = this._displayables.barGroup; + var size = this._size; + var dataZoomModel = this.dataZoomModel; + + barGroup.add(displaybles.filler = new Rect({ + draggable: true, + cursor: 'move', + drift: bind(this._onDragMove, this, 'all'), + ondragstart: bind(this._showDataInfo, this, true), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false), + style: { + fill: dataZoomModel.get('fillerColor'), + textPosition : 'inside' + } + })); + + // Frame border. + barGroup.add(new Rect(graphic.subPixelOptimizeRect({ + silent: true, + shape: { + x: 0, + y: 0, + width: size[0], + height: size[1] + }, + style: { + stroke: dataZoomModel.get('dataBackgroundColor') + || dataZoomModel.get('borderColor'), + lineWidth: DEFAULT_FRAME_BORDER_WIDTH, + fill: 'rgba(0,0,0,0)' + } + }))); + + var iconStr = dataZoomModel.get('handleIcon'); + each([0, 1], function (handleIndex) { + var path = graphic.makePath(iconStr, { + style: { + strokeNoScale: true + }, + rectHover: true, + cursor: this._orient === 'vertical' ? 'ns-resize' : 'ew-resize', + draggable: true, + drift: bind(this._onDragMove, this, handleIndex), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false) + }, { + x: -0.5, + y: 0, + width: 1, + height: 1 + }, 'center'); + + var bRect = path.getBoundingRect(); + this._handleHeight = numberUtil.parsePercent(dataZoomModel.get('handleSize'), this._size[1]); + this._handleWidth = bRect.width / bRect.height * this._handleHeight; + + path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle()); + var handleColor = dataZoomModel.get('handleColor'); + // Compatitable with previous version + if (handleColor != null) { + path.style.fill = handleColor; + } + + barGroup.add(handles[handleIndex] = path); + + var textStyleModel = dataZoomModel.textStyleModel; + + this.group.add( + handleLabels[handleIndex] = new graphic.Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textVerticalAlign: 'middle', + textAlign: 'center', + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + }, + z2: 10 + })); + + }, this); + }, + + /** + * @private + */ + _resetInterval: function () { + var range = this._range = this.dataZoomModel.getPercentRange(); + var viewExtent = this._getViewExtent(); + + this._handleEnds = [ + linearMap(range[0], [0, 100], viewExtent, true), + linearMap(range[1], [0, 100], viewExtent, true) + ]; + }, + + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} dx + * @param {number} dy + */ + _updateInterval: function (handleIndex, delta) { + var handleEnds = this._handleEnds; + var viewExtend = this._getViewExtent(); + + sliderMove( + delta, + handleEnds, + viewExtend, + (handleIndex === 'all' || this.dataZoomModel.get('zoomLock')) + ? 'rigid' : 'cross', + handleIndex + ); + + this._range = asc([ + linearMap(handleEnds[0], viewExtend, [0, 100], true), + linearMap(handleEnds[1], viewExtend, [0, 100], true) + ]); + }, + + /** + * @private + */ + _updateView: function () { + var displaybles = this._displayables; + var handleEnds = this._handleEnds; + var handleInterval = asc(handleEnds.slice()); + var size = this._size; + + each([0, 1], function (handleIndex) { + // Handles + var handle = displaybles.handles[handleIndex]; + var handleHeight = this._handleHeight; + handle.attr({ + scale: [handleHeight, handleHeight], + position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2] + }); + }, this); + + // Filler + displaybles.filler.setShape({ + x: handleInterval[0], + y: 0, + width: handleInterval[1] - handleInterval[0], + height: size[1] + }); + + this._updateDataInfo(); + }, + + /** + * @private + */ + _updateDataInfo: function () { + var dataZoomModel = this.dataZoomModel; + var displaybles = this._displayables; + var handleLabels = displaybles.handleLabels; + var orient = this._orient; + var labelTexts = ['', '']; + + // FIXME + // date型,支持formatter,autoformatter(ec2 date.getAutoFormatter) + if (dataZoomModel.get('showDetail')) { + var dataInterval; + var axis; + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + // Using dataInterval of the first axis. + if (!dataInterval) { + dataInterval = dataZoomModel + .getAxisProxy(dimNames.name, axisIndex) + .getDataValueWindow(); + axis = this.ecModel.getComponent(dimNames.axis, axisIndex).axis; + } + }, this); + + if (dataInterval) { + labelTexts = [ + this._formatLabel(dataInterval[0], axis), + this._formatLabel(dataInterval[1], axis) + ]; + } + } + + var orderedHandleEnds = asc(this._handleEnds.slice()); + + setLabel.call(this, 0); + setLabel.call(this, 1); + + function setLabel(handleIndex) { + // Label + // Text should not transform by barGroup. + // Ignore handlers transform + var barTransform = graphic.getTransform( + displaybles.handles[handleIndex].parent, this.group + ); + var direction = graphic.transformDirection( + handleIndex === 0 ? 'right' : 'left', barTransform + ); + var offset = this._handleWidth / 2 + LABEL_GAP; + var textPoint = graphic.applyTransform( + [ + orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), + this._size[1] / 2 + ], + barTransform + ); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction, + textAlign: orient === HORIZONTAL ? direction : 'center', + text: labelTexts[handleIndex] + }); + } + }, + + /** + * @private + */ + _formatLabel: function (value, axis) { + var dataZoomModel = this.dataZoomModel; + var labelFormatter = dataZoomModel.get('labelFormatter'); + if (zrUtil.isFunction(labelFormatter)) { + return labelFormatter(value); + } + + var labelPrecision = dataZoomModel.get('labelPrecision'); + if (labelPrecision == null || labelPrecision === 'auto') { + labelPrecision = axis.getPixelPrecision(); + } + + value = (value == null && isNaN(value)) + ? '' + // FIXME Glue code + : (axis.type === 'category' || axis.type === 'time') + ? axis.scale.getLabel(Math.round(value)) + // param of toFixed should less then 20. + : value.toFixed(Math.min(labelPrecision, 20)); + + if (zrUtil.isString(labelFormatter)) { + value = labelFormatter.replace('{value}', value); + } + + return value; + }, + + /** + * @private + * @param {boolean} showOrHide true: show, false: hide + */ + _showDataInfo: function (showOrHide) { + // Always show when drgging. + showOrHide = this._dragging || showOrHide; + + var handleLabels = this._displayables.handleLabels; + handleLabels[0].attr('invisible', !showOrHide); + handleLabels[1].attr('invisible', !showOrHide); + }, + + _onDragMove: function (handleIndex, dx, dy) { + this._dragging = true; + + // Transform dx, dy to bar coordination. + var vertex = this._applyBarTransform([dx, dy], true); + + this._updateInterval(handleIndex, vertex[0]); + this._updateView(); + + if (this.dataZoomModel.get('realtime')) { + this._dispatchZoomAction(); + } + }, + + _onDragEnd: function () { + this._dragging = false; + this._showDataInfo(false); + this._dispatchZoomAction(); + }, + + /** + * This action will be throttled. + * @private + */ + _dispatchZoomAction: function () { + var range = this._range; + + this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + dataZoomId: this.dataZoomModel.id, + start: range[0], + end: range[1] + }); + }, + + /** + * @private + */ + _applyBarTransform: function (vertex, inverse) { + var barTransform = this._displayables.barGroup.getLocalTransform(); + return graphic.applyTransform(vertex, barTransform, inverse); + }, + + /** + * @private + */ + _findCoordRect: function () { + // Find the grid coresponding to the first axis referred by dataZoom. + var targetInfo = this.getTargetInfo(); + + // FIXME + // 判断是catesian还是polar + var rect; + if (targetInfo.cartesians.length) { + rect = targetInfo.cartesians[0].model.coordinateSystem.getRect(); + } + else { // Polar + // FIXME + // 暂时随便写的 + var width = this.api.getWidth(); + var height = this.api.getHeight(); + rect = { + x: width * 0.2, + y: height * 0.2, + width: width * 0.6, + height: height * 0.6 + }; + } + + return rect; + } + + }); + + function getOtherDim(thisDim) { + // FIXME + // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好 + return thisDim === 'x' ? 'y' : 'x'; + } + + module.exports = SliderZoomView; + + + +/***/ }, +/* 324 */ +/***/ function(module, exports) { + + + + /** + * Calculate slider move result. + * + * @param {number} delta Move length. + * @param {Array.} handleEnds handleEnds[0] and be bigger then handleEnds[1]. + * handleEnds will be modified in this method. + * @param {Array.} extent handleEnds is restricted by extent. + * extent[0] should less or equals than extent[1]. + * @param {string} mode 'rigid': Math.abs(handleEnds[0] - handleEnds[1]) remain unchanged, + * 'cross' handleEnds[0] can be bigger then handleEnds[1], + * 'push' handleEnds[0] can not be bigger then handleEnds[1], + * when they touch, one push other. + * @param {number} handleIndex If mode is 'rigid', handleIndex is not required. + * @param {Array.} The input handleEnds. + */ + module.exports = function (delta, handleEnds, extent, mode, handleIndex) { + if (!delta) { + return handleEnds; + } + + if (mode === 'rigid') { + delta = getRealDelta(delta, handleEnds, extent); + handleEnds[0] += delta; + handleEnds[1] += delta; + } + else { + delta = getRealDelta(delta, handleEnds[handleIndex], extent); + handleEnds[handleIndex] += delta; + + if (mode === 'push' && handleEnds[0] > handleEnds[1]) { + handleEnds[1 - handleIndex] = handleEnds[handleIndex]; + } + } + + return handleEnds; + + function getRealDelta(delta, handleEnds, extent) { + var handleMinMax = !handleEnds.length + ? [handleEnds, handleEnds] + : handleEnds.slice(); + handleEnds[0] > handleEnds[1] && handleMinMax.reverse(); + + if (delta < 0 && handleMinMax[0] + delta < extent[0]) { + delta = extent[0] - handleMinMax[0]; + } + if (delta > 0 && handleMinMax[1] + delta > extent[1]) { + delta = extent[1] - handleMinMax[1]; + } + return delta; + } + }; + + +/***/ }, +/* 325 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + module.exports = __webpack_require__(318).extend({ + + type: 'dataZoom.inside', + + /** + * @protected + */ + defaultOption: { + zoomLock: false // Whether disable zoom but only pan. + } + }); + + +/***/ }, +/* 326 */ +/***/ function(module, exports, __webpack_require__) { + + + + var DataZoomView = __webpack_require__(321); + var zrUtil = __webpack_require__(4); + var sliderMove = __webpack_require__(324); + var roams = __webpack_require__(327); + var bind = zrUtil.bind; + + var InsideZoomView = DataZoomView.extend({ + + type: 'dataZoom.inside', + + /** + * @override + */ + init: function (ecModel, api) { + /** + * 'throttle' is used in this.dispatchAction, so we save range + * to avoid missing some 'pan' info. + * @private + * @type {Array.} + */ + this._range; + }, + + /** + * @override + */ + render: function (dataZoomModel, ecModel, api, payload) { + InsideZoomView.superApply(this, 'render', arguments); + + // Notice: origin this._range should be maintained, and should not be re-fetched + // from dataZoomModel when payload.type is 'dataZoom', otherwise 'pan' or 'zoom' + // info will be missed because of 'throttle' of this.dispatchAction. + if (roams.shouldRecordRange(payload, dataZoomModel.id)) { + this._range = dataZoomModel.getPercentRange(); + } + + // Reset controllers. + var coordInfoList = this.getTargetInfo().cartesians; + var allCoordIds = zrUtil.map(coordInfoList, function (coordInfo) { + return roams.generateCoordId(coordInfo.model); + }); + + zrUtil.each(coordInfoList, function (coordInfo) { + var coordModel = coordInfo.model; + roams.register( + api, + { + coordId: roams.generateCoordId(coordModel), + allCoordIds: allCoordIds, + coordinateSystem: coordModel.coordinateSystem, + dataZoomId: dataZoomModel.id, + throttleRate: dataZoomModel.get('throttle', true), + panGetRange: bind(this._onPan, this, coordInfo), + zoomGetRange: bind(this._onZoom, this, coordInfo) + } + ); + }, this); + + // TODO + // polar支持 + }, + + /** + * @override + */ + dispose: function () { + roams.unregister(this.api, this.dataZoomModel.id); + InsideZoomView.superApply(this, 'dispose', arguments); + this._range = null; + }, + + /** + * @private + */ + _onPan: function (coordInfo, controller, dx, dy) { + return ( + this._range = panCartesian( + [dx, dy], this._range, controller, coordInfo + ) + ); + }, + + /** + * @private + */ + _onZoom: function (coordInfo, controller, scale, mouseX, mouseY) { + var dataZoomModel = this.dataZoomModel; + + if (dataZoomModel.option.zoomLock) { + return this._range; + } + + return ( + this._range = scaleCartesian( + 1 / scale, [mouseX, mouseY], this._range, + controller, coordInfo, dataZoomModel + ) + ); + } + + }); + + function panCartesian(pixelDeltas, range, controller, coordInfo) { + range = range.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo(pixelDeltas, axisModel, controller); + + var percentDelta = directionInfo.signal + * (range[1] - range[0]) + * directionInfo.pixel / directionInfo.pixelLength; + + sliderMove( + percentDelta, + range, + [0, 100], + 'rigid' + ); + + return range; + } + + function scaleCartesian(scale, mousePoint, range, controller, coordInfo, dataZoomModel) { + range = range.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo(mousePoint, axisModel, controller); + + var mouse = directionInfo.pixel - directionInfo.pixelStart; + var percentPoint = mouse / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; + + scale = Math.max(scale, 0); + range[0] = (range[0] - percentPoint) * scale + percentPoint; + range[1] = (range[1] - percentPoint) * scale + percentPoint; + + return fixRange(range); + } + + function getDirectionInfo(xy, axisModel, controller) { + var axis = axisModel.axis; + var rect = controller.rectProvider(); + var ret = {}; + + if (axis.dim === 'x') { + ret.pixel = xy[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } + else { // axis.dim === 'y' + ret.pixel = xy[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + + return ret; + } + + function fixRange(range) { + // Clamp, using !(<= or >=) to handle NaN. + // jshint ignore:start + var bound = [0, 100]; + !(range[0] <= bound[1]) && (range[0] = bound[1]); + !(range[1] <= bound[1]) && (range[1] = bound[1]); + !(range[0] >= bound[0]) && (range[0] = bound[0]); + !(range[1] >= bound[0]) && (range[1] = bound[0]); + // jshint ignore:end + + return range; + } + + module.exports = InsideZoomView; + + +/***/ }, +/* 327 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Roam controller manager. + */ + + + // Only create one roam controller for each coordinate system. + // one roam controller might be refered by two inside data zoom + // components (for example, one for x and one for y). When user + // pan or zoom, only dispatch one action for those data zoom + // components. + + var zrUtil = __webpack_require__(4); + var RoamController = __webpack_require__(174); + var throttle = __webpack_require__(308); + var curry = zrUtil.curry; + + var ATTR = '\0_ec_dataZoom_roams'; + + var roams = { + + /** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {Object} dataZoomInfo + * @param {string} dataZoomInfo.coordId + * @param {Object} dataZoomInfo.coordinateSystem + * @param {Array.} dataZoomInfo.allCoordIds + * @param {string} dataZoomInfo.dataZoomId + * @param {number} dataZoomInfo.throttleRate + * @param {Function} dataZoomInfo.panGetRange + * @param {Function} dataZoomInfo.zoomGetRange + */ + register: function (api, dataZoomInfo) { + var store = giveStore(api); + var theDataZoomId = dataZoomInfo.dataZoomId; + var theCoordId = dataZoomInfo.coordId; + + // Do clean when a dataZoom changes its target coordnate system. + zrUtil.each(store, function (record, coordId) { + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[theDataZoomId] + && zrUtil.indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0 + ) { + delete dataZoomInfos[theDataZoomId]; + record.count--; + } + }); + + cleanStore(store); + + var record = store[theCoordId]; + + // Create if needed. + if (!record) { + record = store[theCoordId] = { + coordId: theCoordId, + dataZoomInfos: {}, + count: 0 + }; + record.controller = createController(api, dataZoomInfo, record); + record.dispatchAction = zrUtil.curry(dispatchAction, api); + } + + // Consider resize, area should be always updated. + var rect = dataZoomInfo.coordinateSystem.getRect().clone(); + record.controller.rectProvider = function () { + return rect; + }; + + // Update throttle. + throttle.createOrUpdate( + record, + 'dispatchAction', + dataZoomInfo.throttleRate, + 'fixRate' + ); + + // Update reference of dataZoom. + !(record.dataZoomInfos[theDataZoomId]) && record.count++; + record.dataZoomInfos[theDataZoomId] = dataZoomInfo; + }, + + /** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {string} dataZoomId + */ + unregister: function (api, dataZoomId) { + var store = giveStore(api); + + zrUtil.each(store, function (record) { + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[dataZoomId]) { + delete dataZoomInfos[dataZoomId]; + record.count--; + } + }); + + cleanStore(store); + }, + + /** + * @public + */ + shouldRecordRange: function (payload, dataZoomId) { + if (payload && payload.type === 'dataZoom' && payload.batch) { + for (var i = 0, len = payload.batch.length; i < len; i++) { + if (payload.batch[i].dataZoomId === dataZoomId) { + return false; + } + } + } + return true; + }, + + /** + * @public + */ + generateCoordId: function (coordModel) { + return coordModel.type + '\0_' + coordModel.id; + } + }; + + /** + * Key: coordId, value: {dataZoomInfos: [], count, controller} + * @type {Array.} + */ + function giveStore(api) { + // Mount store on zrender instance, so that we do not + // need to worry about dispose. + var zr = api.getZr(); + return zr[ATTR] || (zr[ATTR] = {}); + } + + function createController(api, dataZoomInfo, newRecord) { + var controller = new RoamController(api.getZr()); + controller.enable(); + controller.on('pan', curry(onPan, newRecord)); + controller.on('zoom', curry(onZoom, newRecord)); + + return controller; + } + + function cleanStore(store) { + zrUtil.each(store, function (record, coordId) { + if (!record.count) { + record.controller.off('pan').off('zoom'); + delete store[coordId]; + } + }); + } + + function onPan(record, dx, dy) { + wrapAndDispatch(record, function (info) { + return info.panGetRange(record.controller, dx, dy); + }); + } + + function onZoom(record, scale, mouseX, mouseY) { + wrapAndDispatch(record, function (info) { + return info.zoomGetRange(record.controller, scale, mouseX, mouseY); + }); + } + + function wrapAndDispatch(record, getRange) { + var batch = []; + + zrUtil.each(record.dataZoomInfos, function (info) { + var range = getRange(info); + range && batch.push({ + dataZoomId: info.dataZoomId, + start: range[0], + end: range[1] + }); + }); + + record.dispatchAction(batch); + } + + /** + * This action will be throttled. + */ + function dispatchAction(api, batch) { + api.dispatchAction({ + type: 'dataZoom', + batch: batch + }); + } + + module.exports = roams; + + + +/***/ }, +/* 328 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom processor + */ + + + var echarts = __webpack_require__(1); + + echarts.registerProcessor(function (ecModel, api) { + + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // We calculate window and reset axis here but not in model + // init stage and not after action dispatch handler, because + // reset should be called after seriesData.restoreData. + dataZoomModel.eachTargetAxis(resetSingleAxis); + + // Caution: data zoom filtering is order sensitive when using + // percent range and no min/max/scale set on axis. + // For example, we have dataZoom definition: + // [ + // {xAxisIndex: 0, start: 30, end: 70}, + // {yAxisIndex: 0, start: 20, end: 80} + // ] + // In this case, [20, 80] of y-dataZoom should be based on data + // that have filtered by x-dataZoom using range of [30, 70], + // but should not be based on full raw data. Thus sliding + // x-dataZoom will change both ranges of xAxis and yAxis, + // while sliding y-dataZoom will only change the range of yAxis. + // So we should filter x-axis after reset x-axis immediately, + // and then reset y-axis and filter y-axis. + dataZoomModel.eachTargetAxis(filterSingleAxis); + }); + + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // Fullfill all of the range props so that user + // is able to get them from chart.getOption(). + var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); + var percentRange = axisProxy.getDataPercentWindow(); + var valueRange = axisProxy.getDataValueWindow(); + + dataZoomModel.setRawRange({ + start: percentRange[0], + end: percentRange[1], + startValue: valueRange[0], + endValue: valueRange[1] + }); + }); + }); + + function resetSingleAxis(dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel); + } + + function filterSingleAxis(dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel); + } + + + + +/***/ }, +/* 329 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom action + */ + + + var zrUtil = __webpack_require__(4); + var helper = __webpack_require__(319); + var echarts = __webpack_require__(1); + + + echarts.registerAction('dataZoom', function (payload, ecModel) { + + var linkedNodesFinder = helper.createLinkedNodesFinder( + zrUtil.bind(ecModel.eachComponent, ecModel, 'dataZoom'), + helper.eachAxisDim, + function (model, dimNames) { + return model.get(dimNames.axisIndex); + } + ); + + var effectedModels = []; + + ecModel.eachComponent( + {mainType: 'dataZoom', query: payload}, + function (model, index) { + effectedModels.push.apply( + effectedModels, linkedNodesFinder(model).nodes + ); + } + ); + + zrUtil.each(effectedModels, function (dataZoomModel, index) { + dataZoomModel.setRawRange({ + start: payload.start, + end: payload.end, + startValue: payload.startValue, + endValue: payload.endValue + }); + }); + + }); + + + +/***/ }, +/* 330 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * visualMap component entry + */ + + + __webpack_require__(331); + __webpack_require__(342); + + + +/***/ }, +/* 331 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(332) + ); + + __webpack_require__(333); + __webpack_require__(334); + __webpack_require__(335); + __webpack_require__(338); + __webpack_require__(341); + + + +/***/ }, +/* 332 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file VisualMap preprocessor + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + module.exports = function (option) { + var visualMap = option && option.visualMap; + + if (!zrUtil.isArray(visualMap)) { + visualMap = visualMap ? [visualMap] : []; + } + + each(visualMap, function (opt) { + if (!opt) { + return; + } + + // rename splitList to pieces + if (has(opt, 'splitList') && !has(opt, 'pieces')) { + opt.pieces = opt.splitList; + delete opt.splitList; + } + + var pieces = opt.pieces; + if (pieces && zrUtil.isArray(pieces)) { + each(pieces, function (piece) { + if (zrUtil.isObject(piece)) { + if (has(piece, 'start') && !has(piece, 'min')) { + piece.min = piece.start; + } + if (has(piece, 'end') && !has(piece, 'max')) { + piece.max = piece.end; + } + } + }); + } + }); + }; + + function has(obj, name) { + return obj && obj.hasOwnProperty && obj.hasOwnProperty(name); + } + + + +/***/ }, +/* 333 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('visualMap', function (option) { + // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used. + return ( + !option.categories + && ( + !( + option.pieces + ? option.pieces.length > 0 + : option.splitNumber > 0 + ) + || option.calculable + ) + ) + ? 'continuous' : 'piecewise'; + }); + + + +/***/ }, +/* 334 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data range visual coding. + */ + + + var echarts = __webpack_require__(1); + var visualSolution = __webpack_require__(306); + var VisualMapping = __webpack_require__(192); + + echarts.registerVisual(echarts.PRIORITY.VISUAL.COMPONENT, function (ecModel) { + ecModel.eachComponent('visualMap', function (visualMapModel) { + processSingleVisualMap(visualMapModel, ecModel); + }); + + prepareVisualMeta(ecModel); + }); + + function processSingleVisualMap(visualMapModel, ecModel) { + visualMapModel.eachTargetSeries(function (seriesModel) { + var data = seriesModel.getData(); + + visualSolution.applyVisual( + visualMapModel.stateList, + visualMapModel.targetVisuals, + data, + visualMapModel.getValueState, + visualMapModel, + visualMapModel.getDataDimension(data) + ); + }); + } + + // Only support color. + function prepareVisualMeta(ecModel) { + ecModel.eachSeries(function (seriesModel) { + var data = seriesModel.getData(); + var visualMetaList = []; + + ecModel.eachComponent('visualMap', function (visualMapModel) { + if (visualMapModel.isTargetSeries(seriesModel)) { + var visualMeta = {}; + visualMetaList.push(visualMeta); + visualMeta.stops = visualMapModel.getStops(seriesModel, getColorVisual); + visualMeta.dimension = visualMapModel.getDataDimension(data); + } + }); + + // console.log(JSON.stringify(visualMetaList.map(a => a.stops))); + seriesModel.getData().setVisual('visualMeta', visualMetaList); + }); + } + + // FIXME + // performance and export for heatmap? + function getColorVisual(visualMapModel, value, valueState) { + var mappings = visualMapModel.targetVisuals[valueState]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + var resultVisual = {}; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + var mapping = mappings[ + type === 'colorAlpha' ? '__alphaForOpacity' : type + ]; + mapping && mapping.applyVisual(value, getVisual, setVisual); + } + + return resultVisual.color; + + function getVisual(key) { + return resultVisual[key]; + } + + function setVisual(key, value) { + resultVisual[key] = value; + } + } + + + + +/***/ }, +/* 335 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var VisualMapModel = __webpack_require__(336); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + // Constant + var DEFAULT_BAR_BOUND = [20, 140]; + + var ContinuousModel = VisualMapModel.extend({ + + type: 'visualMap.continuous', + + /** + * @protected + */ + defaultOption: { + align: 'auto', // 'auto', 'left', 'right', 'top', 'bottom' + calculable: false, // This prop effect default component type determine, + // See echarts/component/visualMap/typeDefaulter. + range: null, // selected range. In default case `range` is [min, max] + // and can auto change along with modification of min max, + // util use specifid a range. + realtime: true, // Whether realtime update. + itemHeight: null, // The length of the range control edge. + itemWidth: null, // The length of the other side. + hoverLink: true, // Enable hover highlight. + hoverLinkDataSize: null,// The size of hovered data. + hoverLinkOnHandle: true // Whether trigger hoverLink when hover handle. + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + ContinuousModel.superApply(this, 'optionUpdated', arguments); + + this.resetTargetSeries(); + this.resetExtent(); + + this.resetVisual(function (mappingOption) { + mappingOption.mappingMethod = 'linear'; + mappingOption.dataExtent = this.getExtent(); + }); + + this._resetRange(); + }, + + /** + * @protected + * @override + */ + resetItemSize: function () { + ContinuousModel.superApply(this, 'resetItemSize', arguments); + + var itemSize = this.itemSize; + + this._orient === 'horizontal' && itemSize.reverse(); + + (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]); + (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]); + }, + + /** + * @private + */ + _resetRange: function () { + var dataExtent = this.getExtent(); + var range = this.option.range; + + if (!range || range.auto) { + // `range` should always be array (so we dont use other + // value like 'auto') for user-friend. (consider getOption). + dataExtent.auto = 1; + this.option.range = dataExtent; + } + else if (zrUtil.isArray(range)) { + if (range[0] > range[1]) { + range.reverse(); + } + range[0] = Math.max(range[0], dataExtent[0]); + range[1] = Math.min(range[1], dataExtent[1]); + } + }, + + /** + * @protected + * @override + */ + completeVisualOption: function () { + VisualMapModel.prototype.completeVisualOption.apply(this, arguments); + + zrUtil.each(this.stateList, function (state) { + var symbolSize = this.option.controller[state].symbolSize; + if (symbolSize && symbolSize[0] !== symbolSize[1]) { + symbolSize[0] = 0; // For good looking. + } + }, this); + }, + + /** + * @override + */ + setSelected: function (selected) { + this.option.range = selected.slice(); + this._resetRange(); + }, + + /** + * @public + */ + getSelected: function () { + var dataExtent = this.getExtent(); + + var dataInterval = numberUtil.asc( + (this.get('range') || []).slice() + ); + + // Clamp + dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]); + dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]); + dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]); + dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]); + + return dataInterval; + }, + + /** + * @override + */ + getValueState: function (value) { + var range = this.option.range; + var dataExtent = this.getExtent(); + + // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'. + // range[1] is processed likewise. + return ( + (range[0] <= dataExtent[0] || range[0] <= value) + && (range[1] >= dataExtent[1] || value <= range[1]) + ) ? 'inRange' : 'outOfRange'; + }, + + /** + * @params {Array.} range target value: range[0] <= value && value <= range[1] + * @return {Array.} [{seriesId, dataIndices: >}, ...] + */ + findTargetDataIndices: function (range) { + var result = []; + + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + + data.each(this.getDataDimension(data), function (value, dataIndex) { + range[0] <= value && value <= range[1] && dataIndices.push(dataIndex); + }, true, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + getStops: function (seriesModel, getColorVisual) { + var result = []; + insertStopList(this, 'outOfRange', this.getExtent(), result); + insertStopList(this, 'inRange', this.option.range.slice(), result); + + zrUtil.each(result, function (item) { + item.color = getColorVisual(this, item.value, item.valueState); + }, this); + + return result; + } + + }); + + function getColorStopValues(visualMapModel, valueState, dataExtent) { + var mapping = visualMapModel.targetVisuals[valueState].color; + + if (!mapping) { + return dataExtent.slice(); + } + + var count = mapping.option.visual.length; + + if (count <= 1 || dataExtent[0] === dataExtent[1]) { + return dataExtent.slice(); + } + + // We only use linear mappping for color, so we can do inverse mapping: + var step = (dataExtent[1] - dataExtent[0]) / (count - 1); + var value = dataExtent[0]; + var stopValues = []; + for (var i = 0; i < count && value < dataExtent[1]; i++) { + stopValues.push(value); + value += step; + } + stopValues.push(dataExtent[1]); + + return stopValues; + } + + function insertStopList(visualMapModel, valueState, dataExtent, result) { + var stops = getColorStopValues(visualMapModel, valueState, dataExtent); + + zrUtil.each(stops, function (val) { + var stop = {value: val, valueState: valueState}; + var inRange = 0; + for (var i = 0; i < result.length; i++) { + // Format to: outOfRange -- inRange -- outOfRange. + inRange |= result[i].valueState === 'inRange'; + if (val < result[i].value) { + result.splice(i, 0, stop); + return; + } + inRange && (result[i].valueState = 'inRange'); + } + result.push(stop); + }); + } + + module.exports = ContinuousModel; + + + +/***/ }, +/* 336 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Controller visual map model + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + var visualDefault = __webpack_require__(337); + var VisualMapping = __webpack_require__(192); + var visualSolution = __webpack_require__(306); + var mapVisual = VisualMapping.mapVisual; + var modelUtil = __webpack_require__(5); + var eachVisual = VisualMapping.eachVisual; + var numberUtil = __webpack_require__(7); + var isArray = zrUtil.isArray; + var each = zrUtil.each; + var asc = numberUtil.asc; + var linearMap = numberUtil.linearMap; + var noop = zrUtil.noop; + + var DEFAULT_COLOR = ['#f6efa6', '#d88273', '#bf444c']; + + var VisualMapModel = echarts.extendComponentModel({ + + type: 'visualMap', + + dependencies: ['series'], + + /** + * @readOnly + * @type {Array.} + */ + stateList: ['inRange', 'outOfRange'], + + /** + * @readOnly + * @type {Array.} + */ + replacableOptionKeys: [ + 'inRange', 'outOfRange', 'target', 'controller', 'color' + ], + + /** + * [lowerBound, upperBound] + * + * @readOnly + * @type {Array.} + */ + dataBound: [-Infinity, Infinity], + + /** + * @readOnly + * @type {string|Object} + */ + layoutMode: {type: 'box', ignoreSize: true}, + + /** + * @protected + */ + defaultOption: { + show: true, + + zlevel: 0, + z: 4, + + seriesIndex: null, // 所控制的series indices,默认所有有value的series. + + // set min: 0, max: 200, only for campatible with ec2. + // In fact min max should not have default value. + min: 0, // min value, must specified if pieces is not specified. + max: 200, // max value, must specified if pieces is not specified. + + dimension: null, + inRange: null, // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha', + // 'symbol', 'symbolSize' + outOfRange: null, // 'color', 'colorHue', 'colorSaturation', + // 'colorLightness', 'colorAlpha', + // 'symbol', 'symbolSize' + + left: 0, // 'center' ¦ 'left' ¦ 'right' ¦ {number} (px) + right: null, // The same as left. + top: null, // 'top' ¦ 'bottom' ¦ 'center' ¦ {number} (px) + bottom: 0, // The same as top. + + itemWidth: null, + itemHeight: null, + inverse: false, + orient: 'vertical', // 'horizontal' ¦ 'vertical' + + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', // 值域边框颜色 + contentColor: '#5793f3', + inactiveColor: '#aaa', + borderWidth: 0, // 值域边框线宽,单位px,默认为0(无边框) + padding: 5, // 值域内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + textGap: 10, // + precision: 0, // 小数精度,默认为0,无小数点 + color: null, //颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange) + + formatter: null, + text: null, // 文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值 + textStyle: { + color: '#333' // 值域文字颜色 + } + }, + + /** + * @protected + */ + init: function (option, parentModel, ecModel) { + + /** + * @private + * @type {Array.} + */ + this._dataExtent; + + /** + * @readOnly + */ + this.targetVisuals = {}; + + /** + * @readOnly + */ + this.controllerVisuals = {}; + + /** + * @readOnly + */ + this.textStyleModel; + + /** + * [width, height] + * @readOnly + * @type {Array.} + */ + this.itemSize; + + this.mergeDefaultAndTheme(option, ecModel); + }, + + /** + * @protected + */ + optionUpdated: function (newOption, isInit) { + var thisOption = this.option; + + // FIXME + // necessary? + // Disable realtime view update if canvas is not supported. + if (!env.canvasSupported) { + thisOption.realtime = false; + } + + !isInit && visualSolution.replaceVisualOption( + thisOption, newOption, this.replacableOptionKeys + ); + + this.textStyleModel = this.getModel('textStyle'); + + this.resetItemSize(); + + this.completeVisualOption(); + }, + + /** + * @protected + */ + resetVisual: function (supplementVisualOption) { + var stateList = this.stateList; + supplementVisualOption = zrUtil.bind(supplementVisualOption, this); + + this.controllerVisuals = visualSolution.createVisualMappings( + this.option.controller, stateList, supplementVisualOption + ); + this.targetVisuals = visualSolution.createVisualMappings( + this.option.target, stateList, supplementVisualOption + ); + }, + + + /** + * @protected + */ + resetTargetSeries: function () { + var thisOption = this.option; + var allSeriesIndex = thisOption.seriesIndex == null; + thisOption.seriesIndex = allSeriesIndex + ? [] : modelUtil.normalizeToArray(thisOption.seriesIndex); + + allSeriesIndex && this.ecModel.eachSeries(function (seriesModel, index) { + thisOption.seriesIndex.push(index); + }); + }, + + /** + * @public + */ + eachTargetSeries: function (callback, context) { + zrUtil.each(this.option.seriesIndex, function (seriesIndex) { + callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex)); + }, this); + }, + + /** + * @pubilc + */ + isTargetSeries: function (seriesModel) { + var is = false; + this.eachTargetSeries(function (model) { + model === seriesModel && (is = true); + }); + return is; + }, + + /** + * @example + * this.formatValueText(someVal); // format single numeric value to text. + * this.formatValueText(someVal, true); // format single category value to text. + * this.formatValueText([min, max]); // format numeric min-max to text. + * this.formatValueText([this.dataBound[0], max]); // using data lower bound. + * this.formatValueText([min, this.dataBound[1]]); // using data upper bound. + * + * @param {number|Array.} value Real value, or this.dataBound[0 or 1]. + * @param {boolean} [isCategory=false] Only available when value is number. + * @param {Array.} edgeSymbols Open-close symbol when value is interval. + * @return {string} + * @protected + */ + formatValueText: function(value, isCategory, edgeSymbols) { + var option = this.option; + var precision = option.precision; + var dataBound = this.dataBound; + var formatter = option.formatter; + var isMinMax; + var textValue; + edgeSymbols = edgeSymbols || ['<', '>']; + + if (zrUtil.isArray(value)) { + value = value.slice(); + isMinMax = true; + } + + textValue = isCategory + ? value + : (isMinMax + ? [toFixed(value[0]), toFixed(value[1])] + : toFixed(value) + ); + + if (zrUtil.isString(formatter)) { + return formatter + .replace('{value}', isMinMax ? textValue[0] : textValue) + .replace('{value2}', isMinMax ? textValue[1] : textValue); + } + else if (zrUtil.isFunction(formatter)) { + return isMinMax + ? formatter(value[0], value[1]) + : formatter(value); + } + + if (isMinMax) { + if (value[0] === dataBound[0]) { + return edgeSymbols[0] + ' ' + textValue[1]; + } + else if (value[1] === dataBound[1]) { + return edgeSymbols[1] + ' ' + textValue[0]; + } + else { + return textValue[0] + ' - ' + textValue[1]; + } + } + else { // Format single value (includes category case). + return textValue; + } + + function toFixed(val) { + return val === dataBound[0] + ? 'min' + : val === dataBound[1] + ? 'max' + : (+val).toFixed(precision); + } + }, + + /** + * @protected + */ + resetExtent: function () { + var thisOption = this.option; + + // Can not calculate data extent by data here. + // Because series and data may be modified in processing stage. + // So we do not support the feature "auto min/max". + + var extent = asc([thisOption.min, thisOption.max]); + + this._dataExtent = extent; + }, + + /** + * @public + * @param {module:echarts/data/List} list + * @return {string} Concrete dimention. If return null/undefined, + * no dimension used. + */ + getDataDimension: function (list) { + var optDim = this.option.dimension; + return optDim != null + ? optDim : list.dimensions.length - 1; + }, + + /** + * @public + * @override + */ + getExtent: function () { + return this._dataExtent.slice(); + }, + + /** + * @protected + */ + completeVisualOption: function () { + var thisOption = this.option; + var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange}; + + var target = thisOption.target || (thisOption.target = {}); + var controller = thisOption.controller || (thisOption.controller = {}); + + zrUtil.merge(target, base); // Do not override + zrUtil.merge(controller, base); // Do not override + + var isCategory = this.isCategory(); + + completeSingle.call(this, target); + completeSingle.call(this, controller); + completeInactive.call(this, target, 'inRange', 'outOfRange'); + // completeInactive.call(this, target, 'outOfRange', 'inRange'); + completeController.call(this, controller); + + function completeSingle(base) { + // Compatible with ec2 dataRange.color. + // The mapping order of dataRange.color is: [high value, ..., low value] + // whereas inRange.color and outOfRange.color is [low value, ..., high value] + // Notice: ec2 has no inverse. + if (isArray(thisOption.color) + // If there has been inRange: {symbol: ...}, adding color is a mistake. + // So adding color only when no inRange defined. + && !base.inRange + ) { + base.inRange = {color: thisOption.color.slice().reverse()}; + } + + // Compatible with previous logic, always give a defautl color, otherwise + // simple config with no inRange and outOfRange will not work. + // Originally we use visualMap.color as the default color, but setOption at + // the second time the default color will be erased. So we change to use + // constant DEFAULT_COLOR. + // If user do not want the defualt color, set inRange: {color: null}. + base.inRange = base.inRange || {color: DEFAULT_COLOR}; + + // If using shortcut like: {inRange: 'symbol'}, complete default value. + each(this.stateList, function (state) { + var visualType = base[state]; + + if (zrUtil.isString(visualType)) { + var defa = visualDefault.get(visualType, 'active', isCategory); + if (defa) { + base[state] = {}; + base[state][visualType] = defa; + } + else { + // Mark as not specified. + delete base[state]; + } + } + }, this); + } + + function completeInactive(base, stateExist, stateAbsent) { + var optExist = base[stateExist]; + var optAbsent = base[stateAbsent]; + + if (optExist && !optAbsent) { + optAbsent = base[stateAbsent] = {}; + each(optExist, function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + + var defa = visualDefault.get(visualType, 'inactive', isCategory); + + if (defa != null) { + optAbsent[visualType] = defa; + + // Compatibable with ec2: + // Only inactive color to rgba(0,0,0,0) can not + // make label transparent, so use opacity also. + if (visualType === 'color' + && !optAbsent.hasOwnProperty('opacity') + && !optAbsent.hasOwnProperty('colorAlpha') + ) { + optAbsent.opacity = [0, 0]; + } + } + }); + } + } + + function completeController(controller) { + var symbolExists = (controller.inRange || {}).symbol + || (controller.outOfRange || {}).symbol; + var symbolSizeExists = (controller.inRange || {}).symbolSize + || (controller.outOfRange || {}).symbolSize; + var inactiveColor = this.get('inactiveColor'); + + each(this.stateList, function (state) { + + var itemSize = this.itemSize; + var visuals = controller[state]; + + // Set inactive color for controller if no other color + // attr (like colorAlpha) specified. + if (!visuals) { + visuals = controller[state] = { + color: isCategory ? inactiveColor : [inactiveColor] + }; + } + + // Consistent symbol and symbolSize if not specified. + if (visuals.symbol == null) { + visuals.symbol = symbolExists + && zrUtil.clone(symbolExists) + || (isCategory ? 'roundRect' : ['roundRect']); + } + if (visuals.symbolSize == null) { + visuals.symbolSize = symbolSizeExists + && zrUtil.clone(symbolSizeExists) + || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]); + } + + // Filter square and none. + visuals.symbol = mapVisual(visuals.symbol, function (symbol) { + return (symbol === 'none' || symbol === 'square') ? 'roundRect' : symbol; + }); + + // Normalize symbolSize + var symbolSize = visuals.symbolSize; + + if (symbolSize != null) { + var max = -Infinity; + // symbolSize can be object when categories defined. + eachVisual(symbolSize, function (value) { + value > max && (max = value); + }); + visuals.symbolSize = mapVisual(symbolSize, function (value) { + return linearMap(value, [0, max], [0, itemSize[0]], true); + }); + } + + }, this); + } + }, + + /** + * @protected + */ + resetItemSize: function () { + this.itemSize = [ + parseFloat(this.get('itemWidth')), + parseFloat(this.get('itemHeight')) + ]; + }, + + /** + * @public + */ + isCategory: function () { + return !!this.option.categories; + }, + + /** + * @public + * @abstract + */ + setSelected: noop, + + /** + * @public + * @abstract + * @param {*|module:echarts/data/List} valueOrData + * @param {number} dataIndex + * @return {string} state See this.stateList + */ + getValueState: noop + + }); + + module.exports = VisualMapModel; + + + +/***/ }, +/* 337 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual mapping. + */ + + + var zrUtil = __webpack_require__(4); + + var visualDefault = { + + /** + * @public + */ + get: function (visualType, key, isCategory) { + var value = zrUtil.clone( + (defaultOption[visualType] || {})[key] + ); + + return isCategory + ? (zrUtil.isArray(value) ? value[value.length - 1] : value) + : value; + } + + }; + + var defaultOption = { + + color: { + active: ['#006edd', '#e0ffff'], + inactive: ['rgba(0,0,0,0)'] + }, + + colorHue: { + active: [0, 360], + inactive: [0, 0] + }, + + colorSaturation: { + active: [0.3, 1], + inactive: [0, 0] + }, + + colorLightness: { + active: [0.9, 0.5], + inactive: [0, 0] + }, + + colorAlpha: { + active: [0.3, 1], + inactive: [0, 0] + }, + + opacity: { + active: [0.3, 1], + inactive: [0, 0] + }, + + symbol: { + active: ['circle', 'roundRect', 'diamond'], + inactive: ['none'] + }, + + symbolSize: { + active: [10, 50], + inactive: [0, 0] + } + }; + + module.exports = visualDefault; + + + + +/***/ }, +/* 338 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapView = __webpack_require__(339); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var sliderMove = __webpack_require__(324); + var LinearGradient = __webpack_require__(79); + var helper = __webpack_require__(340); + var modelUtil = __webpack_require__(5); + + var linearMap = numberUtil.linearMap; + var each = zrUtil.each; + var mathMin = Math.min; + var mathMax = Math.max; + + // Arbitrary value + var HOVER_LINK_SIZE = 12; + var HOVER_LINK_OUT = 6; + + // Notice: + // Any "interval" should be by the order of [low, high]. + // "handle0" (handleIndex === 0) maps to + // low data value: this._dataInterval[0] and has low coord. + // "handle1" (handleIndex === 1) maps to + // high data value: this._dataInterval[1] and has high coord. + // The logic of transform is implemented in this._createBarGroup. + + var ContinuousView = VisualMapView.extend({ + + type: 'visualMap.continuous', + + /** + * @override + */ + init: function () { + + ContinuousView.superApply(this, 'init', arguments); + + /** + * @private + */ + this._shapes = {}; + + /** + * @private + */ + this._dataInterval = []; + + /** + * @private + */ + this._handleEnds = []; + + /** + * @private + */ + this._orient; + + /** + * @private + */ + this._useHandle; + + /** + * @private + */ + this._hoverLinkDataIndices = []; + + /** + * @private + */ + this._dragging; + + /** + * @private + */ + this._hovering; + }, + + /** + * @protected + * @override + */ + doRender: function (visualMapModel, ecModel, api, payload) { + if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) { + this._buildView(); + } + }, + + /** + * @private + */ + _buildView: function () { + this.group.removeAll(); + + var visualMapModel = this.visualMapModel; + var thisGroup = this.group; + + this._orient = visualMapModel.get('orient'); + this._useHandle = visualMapModel.get('calculable'); + + this._resetInterval(); + + this._renderBar(thisGroup); + + var dataRangeText = visualMapModel.get('text'); + this._renderEndsText(thisGroup, dataRangeText, 0); + this._renderEndsText(thisGroup, dataRangeText, 1); + + // Do this for background size calculation. + this._updateView(true); + + // After updating view, inner shapes is built completely, + // and then background can be rendered. + this.renderBackground(thisGroup); + + // Real update view + this._updateView(); + + this._enableHoverLinkToSeries(); + this._enableHoverLinkFromSeries(); + + this.positionGroup(thisGroup); + }, + + /** + * @private + */ + _renderEndsText: function (group, dataRangeText, endsIndex) { + if (!dataRangeText) { + return; + } + + // Compatible with ec2, text[0] map to high value, text[1] map low value. + var text = dataRangeText[1 - endsIndex]; + text = text != null ? text + '' : ''; + + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var itemSize = visualMapModel.itemSize; + + var barGroup = this._shapes.barGroup; + var position = this._applyTransform( + [ + itemSize[0] / 2, + endsIndex === 0 ? -textGap : itemSize[1] + textGap + ], + barGroup + ); + var align = this._applyTransform( + endsIndex === 0 ? 'bottom' : 'top', + barGroup + ); + var orient = this._orient; + var textStyleModel = this.visualMapModel.textStyleModel; + + this.group.add(new graphic.Text({ + style: { + x: position[0], + y: position[1], + textVerticalAlign: orient === 'horizontal' ? 'middle' : align, + textAlign: orient === 'horizontal' ? align : 'center', + text: text, + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + })); + }, + + /** + * @private + */ + _renderBar: function (targetGroup) { + var visualMapModel = this.visualMapModel; + var shapes = this._shapes; + var itemSize = visualMapModel.itemSize; + var orient = this._orient; + var useHandle = this._useHandle; + var itemAlign = helper.getItemAlign(visualMapModel, this.api, itemSize); + var barGroup = shapes.barGroup = this._createBarGroup(itemAlign); + + // Bar + barGroup.add(shapes.outOfRange = createPolygon()); + barGroup.add(shapes.inRange = createPolygon( + null, + useHandle ? 'move' : null, + zrUtil.bind(this._dragHandle, this, 'all', false), + zrUtil.bind(this._dragHandle, this, 'all', true) + )); + + var textRect = visualMapModel.textStyleModel.getTextRect('国'); + var textSize = mathMax(textRect.width, textRect.height); + + // Handle + if (useHandle) { + shapes.handleThumbs = []; + shapes.handleLabels = []; + shapes.handleLabelPoints = []; + + this._createHandle(barGroup, 0, itemSize, textSize, orient, itemAlign); + this._createHandle(barGroup, 1, itemSize, textSize, orient, itemAlign); + } + + this._createIndicator(barGroup, itemSize, textSize, orient); + + targetGroup.add(barGroup); + }, + + /** + * @private + */ + _createHandle: function (barGroup, handleIndex, itemSize, textSize, orient) { + var onDrift = zrUtil.bind(this._dragHandle, this, handleIndex, false); + var onDragEnd = zrUtil.bind(this._dragHandle, this, handleIndex, true); + var handleThumb = createPolygon( + createHandlePoints(handleIndex, textSize), + 'move', + onDrift, + onDragEnd + ); + handleThumb.position[0] = itemSize[0]; + barGroup.add(handleThumb); + + // Text is always horizontal layout but should not be effected by + // transform (orient/inverse). So label is built separately but not + // use zrender/graphic/helper/RectText, and is located based on view + // group (according to handleLabelPoint) but not barGroup. + var textStyleModel = this.visualMapModel.textStyleModel; + var handleLabel = new graphic.Text({ + draggable: true, + drift: onDrift, + ondragend: onDragEnd, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + }); + this.group.add(handleLabel); + + var handleLabelPoint = [ + orient === 'horizontal' + ? textSize / 2 + : textSize * 1.5, + orient === 'horizontal' + ? (handleIndex === 0 ? -(textSize * 1.5) : (textSize * 1.5)) + : (handleIndex === 0 ? -textSize / 2 : textSize / 2) + ]; + + var shapes = this._shapes; + shapes.handleThumbs[handleIndex] = handleThumb; + shapes.handleLabelPoints[handleIndex] = handleLabelPoint; + shapes.handleLabels[handleIndex] = handleLabel; + }, + + /** + * @private + */ + _createIndicator: function (barGroup, itemSize, textSize, orient) { + var indicator = createPolygon([[0, 0]], 'move'); + indicator.position[0] = itemSize[0]; + indicator.attr({invisible: true, silent: true}); + barGroup.add(indicator); + + var textStyleModel = this.visualMapModel.textStyleModel; + var indicatorLabel = new graphic.Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + }); + this.group.add(indicatorLabel); + + var indicatorLabelPoint = [ + orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT + 3, + 0 + ]; + + var shapes = this._shapes; + shapes.indicator = indicator; + shapes.indicatorLabel = indicatorLabel; + shapes.indicatorLabelPoint = indicatorLabelPoint; + }, + + /** + * @private + */ + _dragHandle: function (handleIndex, isEnd, dx, dy) { + if (!this._useHandle) { + return; + } + + this._dragging = !isEnd; + + if (!isEnd) { + // Transform dx, dy to bar coordination. + var vertex = this._applyTransform([dx, dy], this._shapes.barGroup, true); + this._updateInterval(handleIndex, vertex[1]); + + // Considering realtime, update view should be executed + // before dispatch action. + this._updateView(); + } + + // dragEnd do not dispatch action when realtime. + if (isEnd === !this.visualMapModel.get('realtime')) { // jshint ignore:line + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: this._dataInterval.slice() + }); + } + + if (isEnd) { + !this._hovering && this._clearHoverLinkToSeries(); + } + else if (useHoverLinkOnHandle(this.visualMapModel)) { + this._doHoverLinkToSeries(this._handleEnds[handleIndex], false); + } + }, + + /** + * @private + */ + _resetInterval: function () { + var visualMapModel = this.visualMapModel; + + var dataInterval = this._dataInterval = visualMapModel.getSelected(); + var dataExtent = visualMapModel.getExtent(); + var sizeExtent = [0, visualMapModel.itemSize[1]]; + + this._handleEnds = [ + linearMap(dataInterval[0], dataExtent, sizeExtent, true), + linearMap(dataInterval[1], dataExtent, sizeExtent, true) + ]; + }, + + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} dx + * @param {number} dy + */ + _updateInterval: function (handleIndex, delta) { + delta = delta || 0; + var visualMapModel = this.visualMapModel; + var handleEnds = this._handleEnds; + + sliderMove( + delta, + handleEnds, + [0, visualMapModel.itemSize[1]], + handleIndex === 'all' ? 'rigid' : 'push', + handleIndex + ); + var dataExtent = visualMapModel.getExtent(); + var sizeExtent = [0, visualMapModel.itemSize[1]]; + // Update data interval. + this._dataInterval = [ + linearMap(handleEnds[0], sizeExtent, dataExtent, true), + linearMap(handleEnds[1], sizeExtent, dataExtent, true) + ]; + }, + + /** + * @private + */ + _updateView: function (forSketch) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var shapes = this._shapes; + + var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]]; + var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds; + + var visualInRange = this._createBarVisual( + this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange' + ); + var visualOutOfRange = this._createBarVisual( + dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange' + ); + + shapes.inRange + .setStyle({ + fill: visualInRange.barColor, + opacity: visualInRange.opacity + }) + .setShape('points', visualInRange.barPoints); + shapes.outOfRange + .setStyle({ + fill: visualOutOfRange.barColor, + opacity: visualOutOfRange.opacity + }) + .setShape('points', visualOutOfRange.barPoints); + + this._updateHandle(inRangeHandleEnds, visualInRange); + }, + + /** + * @private + */ + _createBarVisual: function (dataInterval, dataExtent, handleEnds, forceState) { + var opts = { + forceState: forceState, + convertOpacityToAlpha: true + }; + var colorStops = this._makeColorGradient(dataInterval, opts); + + var symbolSizes = [ + this.getControllerVisual(dataInterval[0], 'symbolSize', opts), + this.getControllerVisual(dataInterval[1], 'symbolSize', opts) + ]; + var barPoints = this._createBarPoints(handleEnds, symbolSizes); + + return { + barColor: new LinearGradient(0, 0, 0, 1, colorStops), + barPoints: barPoints, + handlesColor: [ + colorStops[0].color, + colorStops[colorStops.length - 1].color + ] + }; + }, + + /** + * @private + */ + _makeColorGradient: function (dataInterval, opts) { + // Considering colorHue, which is not linear, so we have to sample + // to calculate gradient color stops, but not only caculate head + // and tail. + var sampleNumber = 100; // Arbitrary value. + var colorStops = []; + var step = (dataInterval[1] - dataInterval[0]) / sampleNumber; + + colorStops.push({ + color: this.getControllerVisual(dataInterval[0], 'color', opts), + offset: 0 + }); + + for (var i = 1; i < sampleNumber; i++) { + var currValue = dataInterval[0] + step * i; + if (currValue > dataInterval[1]) { + break; + } + colorStops.push({ + color: this.getControllerVisual(currValue, 'color', opts), + offset: i / sampleNumber + }); + } + + colorStops.push({ + color: this.getControllerVisual(dataInterval[1], 'color', opts), + offset: 1 + }); + + return colorStops; + }, + + /** + * @private + */ + _createBarPoints: function (handleEnds, symbolSizes) { + var itemSize = this.visualMapModel.itemSize; + + return [ + [itemSize[0] - symbolSizes[0], handleEnds[0]], + [itemSize[0], handleEnds[0]], + [itemSize[0], handleEnds[1]], + [itemSize[0] - symbolSizes[1], handleEnds[1]] + ]; + }, + + /** + * @private + */ + _createBarGroup: function (itemAlign) { + var orient = this._orient; + var inverse = this.visualMapModel.get('inverse'); + + return new graphic.Group( + (orient === 'horizontal' && !inverse) + ? {scale: itemAlign === 'bottom' ? [1, 1] : [-1, 1], rotation: Math.PI / 2} + : (orient === 'horizontal' && inverse) + ? {scale: itemAlign === 'bottom' ? [-1, 1] : [1, 1], rotation: -Math.PI / 2} + : (orient === 'vertical' && !inverse) + ? {scale: itemAlign === 'left' ? [1, -1] : [-1, -1]} + : {scale: itemAlign === 'left' ? [1, 1] : [-1, 1]} + ); + }, + + /** + * @private + */ + _updateHandle: function (handleEnds, visualInRange) { + if (!this._useHandle) { + return; + } + + var shapes = this._shapes; + var visualMapModel = this.visualMapModel; + var handleThumbs = shapes.handleThumbs; + var handleLabels = shapes.handleLabels; + + each([0, 1], function (handleIndex) { + var handleThumb = handleThumbs[handleIndex]; + handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]); + handleThumb.position[1] = handleEnds[handleIndex]; + + // Update handle label position. + var textPoint = graphic.applyTransform( + shapes.handleLabelPoints[handleIndex], + graphic.getTransform(handleThumb, this.group) + ); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + text: visualMapModel.formatValueText(this._dataInterval[handleIndex]), + textVerticalAlign: 'middle', + textAlign: this._applyTransform( + this._orient === 'horizontal' + ? (handleIndex === 0 ? 'bottom' : 'top') + : 'left', + shapes.barGroup + ) + }); + }, this); + }, + + /** + * @private + * @param {number} cursorValue + * @param {number} textValue + * @param {string} [rangeSymbol] + * @param {number} [halfHoverLinkSize] + */ + _showIndicator: function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var itemSize = visualMapModel.itemSize; + var sizeExtent = [0, itemSize[1]]; + var pos = linearMap(cursorValue, dataExtent, sizeExtent, true); + + var shapes = this._shapes; + var indicator = shapes.indicator; + if (!indicator) { + return; + } + + indicator.position[1] = pos; + indicator.attr('invisible', false); + indicator.setShape('points', createIndicatorPoints( + !!rangeSymbol, halfHoverLinkSize, pos, itemSize[1] + )); + + var opts = {convertOpacityToAlpha: true}; + var color = this.getControllerVisual(cursorValue, 'color', opts); + indicator.setStyle('fill', color); + + // Update handle label position. + var textPoint = graphic.applyTransform( + shapes.indicatorLabelPoint, + graphic.getTransform(indicator, this.group) + ); + + var indicatorLabel = shapes.indicatorLabel; + indicatorLabel.attr('invisible', false); + var align = this._applyTransform('left', shapes.barGroup); + var orient = this._orient; + indicatorLabel.setStyle({ + text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue), + textVerticalAlign: orient === 'horizontal' ? align : 'middle', + textAlign: orient === 'horizontal' ? 'center' : align, + x: textPoint[0], + y: textPoint[1] + }); + }, + + /** + * @private + */ + _enableHoverLinkToSeries: function () { + var self = this; + this._shapes.barGroup + + .on('mousemove', function (e) { + self._hovering = true; + + if (!self._dragging) { + var itemSize = self.visualMapModel.itemSize; + var pos = self._applyTransform( + [e.offsetX, e.offsetY], self._shapes.barGroup, true, true + ); + // For hover link show when hover handle, which might be + // below or upper than sizeExtent. + pos[1] = mathMin(mathMax(0, pos[1]), itemSize[1]); + + self._doHoverLinkToSeries( + pos[1], + 0 <= pos[0] && pos[0] <= itemSize[0] + ); + } + }) + + .on('mouseout', function () { + // When mouse is out of handle, hoverLink still need + // to be displayed when realtime is set as false. + self._hovering = false; + !self._dragging && self._clearHoverLinkToSeries(); + }); + }, + + /** + * @private + */ + _enableHoverLinkFromSeries: function () { + var zr = this.api.getZr(); + + if (this.visualMapModel.option.hoverLink) { + zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this); + zr.on('mouseout', this._hideIndicator, this); + } + else { + this._clearHoverLinkFromSeries(); + } + }, + + /** + * @private + */ + _doHoverLinkToSeries: function (cursorPos, hoverOnBar) { + var visualMapModel = this.visualMapModel; + var itemSize = visualMapModel.itemSize; + + if (!visualMapModel.option.hoverLink) { + return; + } + + var sizeExtent = [0, itemSize[1]]; + var dataExtent = visualMapModel.getExtent(); + + // For hover link show when hover handle, which might be below or upper than sizeExtent. + cursorPos = mathMin(mathMax(sizeExtent[0], cursorPos), sizeExtent[1]); + + var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent); + var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize]; + var cursorValue = linearMap(cursorPos, sizeExtent, dataExtent, true); + var valueRange = [ + linearMap(hoverRange[0], sizeExtent, dataExtent, true), + linearMap(hoverRange[1], sizeExtent, dataExtent, true) + ]; + // Consider data range is out of visualMap range, see test/visualMap-continuous.html, + // where china and india has very large population. + hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity); + hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); + + // Do not show indicator when mouse is over handle, + // otherwise labels overlap, especially when dragging. + if (hoverOnBar) { + if (valueRange[0] === -Infinity) { + this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize); + } + else if (valueRange[1] === Infinity) { + this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize); + } + else { + this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize); + } + } + + // When realtime is set as false, handles, which are in barGroup, + // also trigger hoverLink, which help user to realize where they + // focus on when dragging. (see test/heatmap-large.html) + // When realtime is set as true, highlight will not show when hover + // handle, because the label on handle, which displays a exact value + // but not range, might mislead users. + var oldBatch = this._hoverLinkDataIndices; + var newBatch = []; + if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) { + newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange); + } + + var resultBatches = modelUtil.compressBatches(oldBatch, newBatch); + this._dispatchHighDown('downplay', resultBatches[0]); + this._dispatchHighDown('highlight', resultBatches[1]); + }, + + /** + * @private + */ + _hoverLinkFromSeriesMouseOver: function (e) { + var el = e.target; + + if (!el || el.dataIndex == null) { + return; + } + + var dataModel = el.dataModel || this.ecModel.getSeriesByIndex(el.seriesIndex); + var data = dataModel.getData(el.dataType); + var dim = data.getDimension(this.visualMapModel.getDataDimension(data)); + var value = data.get(dim, el.dataIndex, true); + + if (!isNaN(value)) { + this._showIndicator(value, value); + } + }, + + /** + * @private + */ + _hideIndicator: function () { + var shapes = this._shapes; + shapes.indicator && shapes.indicator.attr('invisible', true); + shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true); + }, + + /** + * @private + */ + _clearHoverLinkToSeries: function () { + this._hideIndicator(); + + var indices = this._hoverLinkDataIndices; + + this._dispatchHighDown('downplay', indices); + + indices.length = 0; + }, + + /** + * @private + */ + _clearHoverLinkFromSeries: function () { + this._hideIndicator(); + + var zr = this.api.getZr(); + zr.off('mouseover', this._hoverLinkFromSeriesMouseOver); + zr.off('mouseout', this._hideIndicator); + }, + + /** + * @private + */ + _applyTransform: function (vertex, element, inverse, global) { + var transform = graphic.getTransform(element, global ? null : this.group); + + return graphic[ + zrUtil.isArray(vertex) ? 'applyTransform' : 'transformDirection' + ](vertex, transform, inverse); + }, + + /** + * @private + */ + _dispatchHighDown: function (type, batch) { + batch && batch.length && this.api.dispatchAction({ + type: type, + batch: batch + }); + }, + + /** + * @override + */ + dispose: function () { + this._clearHoverLinkFromSeries(); + this._clearHoverLinkToSeries(); + }, + + /** + * @override + */ + remove: function () { + this._clearHoverLinkFromSeries(); + this._clearHoverLinkToSeries(); + } + + }); + + function createPolygon(points, cursor, onDrift, onDragEnd) { + return new graphic.Polygon({ + shape: {points: points}, + draggable: !!onDrift, + cursor: cursor, + drift: onDrift, + ondragend: onDragEnd + }); + } + + function createHandlePoints(handleIndex, textSize) { + return handleIndex === 0 + ? [[0, 0], [textSize, 0], [textSize, -textSize]] + : [[0, 0], [textSize, 0], [textSize, textSize]]; + } + + function createIndicatorPoints(isRange, halfHoverLinkSize, pos, extentMax) { + return isRange + ? [ // indicate range + [0, -mathMin(halfHoverLinkSize, mathMax(pos, 0))], + [HOVER_LINK_OUT, 0], + [0, mathMin(halfHoverLinkSize, mathMax(extentMax - pos, 0))] + ] + : [ // indicate single value + [0, 0], [5, -5], [5, 5] + ]; + } + + function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) { + var halfHoverLinkSize = HOVER_LINK_SIZE / 2; + var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize'); + if (hoverLinkDataSize) { + halfHoverLinkSize = linearMap(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2; + } + return halfHoverLinkSize; + } + + function useHoverLinkOnHandle(visualMapModel) { + return !visualMapModel.get('realtime') && visualMapModel.get('hoverLinkOnHandle'); + } + + module.exports = ContinuousView; + + + +/***/ }, +/* 339 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var formatUtil = __webpack_require__(6); + var layout = __webpack_require__(21); + var echarts = __webpack_require__(1); + var VisualMapping = __webpack_require__(192); + + module.exports = echarts.extendComponentView({ + + type: 'visualMap', + + /** + * @readOnly + * @type {Object} + */ + autoPositionValues: {left: 1, right: 1, top: 1, bottom: 1}, + + init: function (ecModel, api) { + /** + * @readOnly + * @type {module:echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @readOnly + * @type {module:echarts/ExtensionAPI} + */ + this.api = api; + + /** + * @readOnly + * @type {module:echarts/component/visualMap/visualMapModel} + */ + this.visualMapModel; + }, + + /** + * @protected + */ + render: function (visualMapModel, ecModel, api, payload) { + this.visualMapModel = visualMapModel; + + if (visualMapModel.get('show') === false) { + this.group.removeAll(); + return; + } + + this.doRender.apply(this, arguments); + }, + + /** + * @protected + */ + renderBackground: function (group) { + var visualMapModel = this.visualMapModel; + var padding = formatUtil.normalizeCssArray(visualMapModel.get('padding') || 0); + var rect = group.getBoundingRect(); + + group.add(new graphic.Rect({ + z2: -1, // Lay background rect on the lowest layer. + silent: true, + shape: { + x: rect.x - padding[3], + y: rect.y - padding[0], + width: rect.width + padding[3] + padding[1], + height: rect.height + padding[0] + padding[2] + }, + style: { + fill: visualMapModel.get('backgroundColor'), + stroke: visualMapModel.get('borderColor'), + lineWidth: visualMapModel.get('borderWidth') + } + })); + }, + + /** + * @protected + * @param {number} targetValue + * @param {string=} visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize' + * @param {Object} [opts] + * @param {string=} [opts.forceState] Specify state, instead of using getValueState method. + * @param {string=} [opts.convertOpacityToAlpha=false] For color gradient in controller widget. + * @return {*} Visual value. + */ + getControllerVisual: function (targetValue, visualCluster, opts) { + opts = opts || {}; + + var forceState = opts.forceState; + var visualMapModel = this.visualMapModel; + var visualObj = {}; + + // Default values. + if (visualCluster === 'symbol') { + visualObj.symbol = visualMapModel.get('itemSymbol'); + } + if (visualCluster === 'color') { + var defaultColor = visualMapModel.get('contentColor'); + visualObj.color = defaultColor; + } + + function getter(key) { + return visualObj[key]; + } + + function setter(key, value) { + visualObj[key] = value; + } + + var mappings = visualMapModel.controllerVisuals[ + forceState || visualMapModel.getValueState(targetValue) + ]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + + zrUtil.each(visualTypes, function (type) { + var visualMapping = mappings[type]; + if (opts.convertOpacityToAlpha && type === 'opacity') { + type = 'colorAlpha'; + visualMapping = mappings.__alphaForOpacity; + } + if (VisualMapping.dependsOn(type, visualCluster)) { + visualMapping && visualMapping.applyVisual( + targetValue, getter, setter + ); + } + }); + + return visualObj[visualCluster]; + }, + + /** + * @protected + */ + positionGroup: function (group) { + var model = this.visualMapModel; + var api = this.api; + + layout.positionGroup( + group, + model.getBoxLayoutParams(), + {width: api.getWidth(), height: api.getHeight()} + ); + }, + + /** + * @protected + * @abstract + */ + doRender: zrUtil.noop + + }); + + + +/***/ }, +/* 340 */ +/***/ function(module, exports, __webpack_require__) { + + + + var layout = __webpack_require__(21); + + var helper = { + + /** + * @param {module:echarts/component/visualMap/VisualMapModel} visualMapModel\ + * @param {module:echarts/ExtensionAPI} api + * @param {Array.} itemSize always [short, long] + * @return {string} 'left' or 'right' or 'top' or 'bottom' + */ + getItemAlign: function (visualMapModel, api, itemSize) { + var modelOption = visualMapModel.option; + var itemAlign = modelOption.align; + + if (itemAlign != null && itemAlign !== 'auto') { + return itemAlign; + } + + // Auto decision align. + var ecSize = {width: api.getWidth(), height: api.getHeight()}; + var realIndex = modelOption.orient === 'horizontal' ? 1 : 0; + + var paramsSet = [ + ['left', 'right', 'width'], + ['top', 'bottom', 'height'] + ]; + var reals = paramsSet[realIndex]; + var fakeValue = [0, null, 10]; + + var layoutInput = {}; + for (var i = 0; i < 3; i++) { + layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i]; + layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]]; + } + + var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex]; + var rect = layout.getLayoutRect(layoutInput, ecSize, modelOption.padding); + + return reals[ + (rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 + < ecSize[rParam[1]] * 0.5 ? 0 : 1 + ]; + } + + }; + + + module.exports = helper; + + + +/***/ }, +/* 341 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data range action + */ + + + var echarts = __webpack_require__(1); + + var actionInfo = { + type: 'selectDataRange', + event: 'dataRangeSelected', + // FIXME use updateView appears wrong + update: 'update' + }; + + echarts.registerAction(actionInfo, function (payload, ecModel) { + + ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) { + model.setSelected(payload.selected); + }); + + }); + + + +/***/ }, +/* 342 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(332) + ); + + __webpack_require__(333); + __webpack_require__(334); + __webpack_require__(343); + __webpack_require__(344); + __webpack_require__(341); + + + +/***/ }, +/* 343 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapModel = __webpack_require__(336); + var zrUtil = __webpack_require__(4); + var VisualMapping = __webpack_require__(192); + + var PiecewiseModel = VisualMapModel.extend({ + + type: 'visualMap.piecewise', + + /** + * Order Rule: + * + * option.categories / option.pieces / option.text / option.selected: + * If !option.inverse, + * Order when vertical: ['top', ..., 'bottom']. + * Order when horizontal: ['left', ..., 'right']. + * If option.inverse, the meaning of + * the order should be reversed. + * + * this._pieceList: + * The order is always [low, ..., high]. + * + * Mapping from location to low-high: + * If !option.inverse + * When vertical, top is high. + * When horizontal, right is high. + * If option.inverse, reverse. + */ + + /** + * @protected + */ + defaultOption: { + selected: null, // Object. If not specified, means selected. + // When pieces and splitNumber: {'0': true, '5': true} + // When categories: {'cate1': false, 'cate3': true} + // When selected === false, means all unselected. + align: 'auto', // 'auto', 'left', 'right' + itemWidth: 20, // When put the controller vertically, it is the length of + // horizontal side of each item. Otherwise, vertical side. + itemHeight: 14, // When put the controller vertically, it is the length of + // vertical side of each item. Otherwise, horizontal side. + itemSymbol: 'roundRect', + pieceList: null, // Each item is Object, with some of those attrs: + // {min, max, lt, gt, lte, gte, value, + // color, colorSaturation, colorAlpha, opacity, + // symbol, symbolSize}, which customize the range or visual + // coding of the certain piece. Besides, see "Order Rule". + categories: null, // category names, like: ['some1', 'some2', 'some3']. + // Attr min/max are ignored when categories set. See "Order Rule" + splitNumber: 5, // If set to 5, auto split five pieces equally. + // If set to 0 and component type not set, component type will be + // determined as "continuous". (It is less reasonable but for ec2 + // compatibility, see echarts/component/visualMap/typeDefaulter) + selectedMode: 'multiple', // Can be 'multiple' or 'single'. + itemGap: 10, // The gap between two items, in px. + hoverLink: true // Enable hover highlight. + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + PiecewiseModel.superApply(this, 'optionUpdated', arguments); + + /** + * The order is always [low, ..., high]. + * [{text: string, interval: Array.}, ...] + * @private + * @type {Array.} + */ + this._pieceList = []; + + this.resetTargetSeries(); + this.resetExtent(); + + /** + * 'pieces', 'categories', 'splitNumber' + * @type {string} + */ + var mode = this._mode = this._determineMode(); + + resetMethods[this._mode].call(this); + + this._resetSelected(newOption, isInit); + + var categories = this.option.categories; + + this.resetVisual(function (mappingOption, state) { + if (mode === 'categories') { + mappingOption.mappingMethod = 'category'; + mappingOption.categories = zrUtil.clone(categories); + } + else { + mappingOption.dataExtent = this.getExtent(); + mappingOption.mappingMethod = 'piecewise'; + mappingOption.pieceList = zrUtil.map(this._pieceList, function (piece) { + var piece = zrUtil.clone(piece); + if (state !== 'inRange') { + piece.visual = null; + } + return piece; + }); + } + }); + }, + + _resetSelected: function (newOption, isInit) { + var thisOption = this.option; + var pieceList = this._pieceList; + + // Selected do not merge but all override. + var selected = (isInit ? thisOption : newOption).selected || {}; + thisOption.selected = selected; + + // Consider 'not specified' means true. + zrUtil.each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (!(key in selected)) { + selected[key] = true; + } + }, this); + + if (thisOption.selectedMode === 'single') { + // Ensure there is only one selected. + var hasSel = false; + + zrUtil.each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (selected[key]) { + hasSel + ? (selected[key] = false) + : (hasSel = true); + } + }, this); + } + // thisOption.selectedMode === 'multiple', default: all selected. + }, + + /** + * @public + */ + getSelectedMapKey: function (piece) { + return this._mode === 'categories' + ? piece.value + '' : piece.index + ''; + }, + + /** + * @public + */ + getPieceList: function () { + return this._pieceList; + }, + + /** + * @private + * @return {string} + */ + _determineMode: function () { + var option = this.option; + + return option.pieces && option.pieces.length > 0 + ? 'pieces' + : this.option.categories + ? 'categories' + : 'splitNumber'; + }, + + /** + * @public + * @override + */ + setSelected: function (selected) { + this.option.selected = zrUtil.clone(selected); + }, + + /** + * @public + * @override + */ + getValueState: function (value) { + var index = VisualMapping.findPieceIndex(value, this._pieceList); + + return index != null + ? (this.option.selected[this.getSelectedMapKey(this._pieceList[index])] + ? 'inRange' : 'outOfRange' + ) + : 'outOfRange'; + }, + + /** + * @public + * @params {number} pieceIndex piece index in visualMapModel.getPieceList() + * @return {Array.} [{seriesId, dataIndices: >}, ...] + */ + findTargetDataIndices: function (pieceIndex) { + var result = []; + + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + + data.each(this.getDataDimension(data), function (value, dataIndex) { + // Should always base on model pieceList, because it is order sensitive. + var pIdx = VisualMapping.findPieceIndex(value, this._pieceList); + pIdx === pieceIndex && dataIndices.push(dataIndex); + }, true, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + /** + * @private + */ + getRepresentValue: function (piece) { + var representValue; + if (this.isCategory()) { + representValue = piece.value; + } + else { + if (piece.value != null) { + representValue = piece.value; + } + else { + var pieceInterval = piece.interval || []; + representValue = (pieceInterval[0] + pieceInterval[1]) / 2; + } + } + return representValue; + }, + + getStops: function (seriesModel, getColorVisual) { + var result = []; + var model = this; + + var curr = -Infinity; + zrUtil.each(this._pieceList, function (piece) { + // Do not support category yet. + var interval = piece.interval; + if (interval) { + interval[0] > curr && setPiece({ + interval: [curr, interval[0]], + valueState: 'outOfRange' + }); + setPiece({ + interval: interval.slice(), + valueState: this.getValueState((interval[0] + interval[1]) / 2) + }); + curr = interval[1]; + } + }, this); + + return result; + + function setPiece(piece) { + result.push(piece); + piece.color = getColorVisual( + model, model.getRepresentValue(piece), piece.valueState + ); + } + } + + }); + + /** + * Key is this._mode + * @type {Object} + * @this {module:echarts/component/viusalMap/PiecewiseMode} + */ + var resetMethods = { + + splitNumber: function () { + var thisOption = this.option; + var pieceList = this._pieceList; + var precision = thisOption.precision; + var dataExtent = this.getExtent(); + var splitNumber = thisOption.splitNumber; + splitNumber = Math.max(parseInt(splitNumber, 10), 1); + thisOption.splitNumber = splitNumber; + + var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; + // Precision auto-adaption + while (+splitStep.toFixed(precision) !== splitStep && precision < 5) { + precision++; + } + thisOption.precision = precision; + splitStep = +splitStep.toFixed(precision); + + for (var i = 0, curr = dataExtent[0]; i < splitNumber; i++, curr += splitStep) { + var max = i === splitNumber - 1 ? dataExtent[1] : (curr + splitStep); + + pieceList.push({ + index: i, + interval: [curr, max], + close: [1, 1] + }); + } + + normalizePieces(pieceList); + + zrUtil.each(pieceList, function (piece) { + piece.text = this.formatValueText(piece.interval); + }, this); + }, + + categories: function () { + var thisOption = this.option; + zrUtil.each(thisOption.categories, function (cate) { + // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。 + // 是否改一致。 + this._pieceList.push({ + text: this.formatValueText(cate, true), + value: cate + }); + }, this); + + // See "Order Rule". + normalizeReverse(thisOption, this._pieceList); + }, + + pieces: function () { + var thisOption = this.option; + var pieceList = this._pieceList; + + zrUtil.each(thisOption.pieces, function (pieceListItem, index) { + + if (!zrUtil.isObject(pieceListItem)) { + pieceListItem = {value: pieceListItem}; + } + + var item = {text: '', index: index}; + + if (pieceListItem.label != null) { + item.text = pieceListItem.label; + } + + if (pieceListItem.hasOwnProperty('value')) { + var value = item.value = pieceListItem.value; + item.interval = [value, value]; + item.close = [1, 1]; + } + else { + // `min` `max` is legacy option. + // `lt` `gt` `lte` `gte` is recommanded. + var interval = item.interval = []; + var close = item.close = [0, 0]; + + var closeList = [1, 0, 1]; + var infinityList = [-Infinity, Infinity]; + + var useMinMax = []; + for (var lg = 0; lg < 2; lg++) { + var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg]; + for (var i = 0; i < 3 && interval[lg] == null; i++) { + interval[lg] = pieceListItem[names[i]]; + close[lg] = closeList[i]; + useMinMax[lg] = i === 2; + } + interval[lg] == null && (interval[lg] = infinityList[lg]); + } + useMinMax[0] && interval[1] === Infinity && (close[0] = 0); + useMinMax[1] && interval[0] === -Infinity && (close[1] = 0); + + if (true) { + if (interval[0] > interval[1]) { + console.warn( + 'Piece ' + index + 'is illegal: ' + interval + + ' lower bound should not greater then uppper bound.' + ); + } + } + + if (interval[0] === interval[1] && close[0] && close[1]) { + // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}], + // we use value to lift the priority when min === max + item.value = interval[0]; + } + } + + item.visual = VisualMapping.retrieveVisuals(pieceListItem); + + pieceList.push(item); + + }, this); + + // See "Order Rule". + normalizeReverse(thisOption, pieceList); + // Only pieces + normalizePieces(pieceList); + + zrUtil.each(pieceList, function (piece) { + var close = piece.close; + var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]]; + piece.text = piece.text || this.formatValueText( + piece.value != null ? piece.value : piece.interval, + false, + edgeSymbols + ); + }, this); + } + }; + + function normalizeReverse(thisOption, pieceList) { + var inverse = thisOption.inverse; + if (thisOption.orient === 'vertical' ? !inverse : inverse) { + pieceList.reverse(); + } + } + + // Reorder, remove duplicate, which are needed when using gradient. + // Not applicable for categories. + function normalizePieces(pieceList) { + pieceList.sort(function (a, b) { + return littleThan(a, b) ? -1 : 1; + }); + + var curr = -Infinity; + for (var i = 0; i < pieceList.length; i++) { + var interval = pieceList[i].interval; + var close = pieceList[i].close; + for (var lg = 0; lg < 2; lg++) { + if (interval[lg] < curr) { + interval[lg] = curr; + close[lg] = 1 - lg; + } + curr = interval[lg]; + } + } + // console.log(JSON.stringify(pieceList.map(a => a.interval))); + + function littleThan(piece, standard, lg) { + lg = lg || 0; + return piece.interval[lg] < standard.interval[lg] + || ( + piece.interval[lg] === standard.interval[lg] + && ( + +piece.close[lg] > standard.close[lg] + || littleThan(piece, standard, 1) + ) + ); + } + } + + module.exports = PiecewiseModel; + + +/***/ }, +/* 344 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapView = __webpack_require__(339); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var symbolCreators = __webpack_require__(106); + var layout = __webpack_require__(21); + var helper = __webpack_require__(340); + + var PiecewiseVisualMapView = VisualMapView.extend({ + + type: 'visualMap.piecewise', + + /** + * @protected + * @override + */ + doRender: function () { + var thisGroup = this.group; + + thisGroup.removeAll(); + + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var textStyleModel = visualMapModel.textStyleModel; + var textFont = textStyleModel.getFont(); + var textFill = textStyleModel.getTextColor(); + var itemAlign = this._getItemAlign(); + var itemSize = visualMapModel.itemSize; + + var viewData = this._getViewData(); + var showLabel = !viewData.endsText; + var showEndsText = !showLabel; + + showEndsText && this._renderEndsText(thisGroup, viewData.endsText[0], itemSize); + + zrUtil.each(viewData.viewPieceList, renderItem, this); + + showEndsText && this._renderEndsText(thisGroup, viewData.endsText[1], itemSize); + + layout.box( + visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap') + ); + + this.renderBackground(thisGroup); + + this.positionGroup(thisGroup); + + function renderItem(item) { + var piece = item.piece; + + var itemGroup = new graphic.Group(); + itemGroup.onclick = zrUtil.bind(this._onItemClick, this, piece); + + this._enableHoverLink(itemGroup, item.indexInModelPieceList); + + var representValue = visualMapModel.getRepresentValue(piece); + + this._createItemSymbol( + itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]] + ); + + if (showLabel) { + var visualState = this.visualMapModel.getValueState(representValue); + + itemGroup.add(new graphic.Text({ + style: { + x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap, + y: itemSize[1] / 2, + text: piece.text, + textVerticalAlign: 'middle', + textAlign: itemAlign, + textFont: textFont, + fill: textFill, + opacity: visualState === 'outOfRange' ? 0.5 : 1 + } + })); + } + + thisGroup.add(itemGroup); + } + }, + + /** + * @private + */ + _enableHoverLink: function (itemGroup, pieceIndex) { + itemGroup + .on('mouseover', zrUtil.bind(onHoverLink, this, 'highlight')) + .on('mouseout', zrUtil.bind(onHoverLink, this, 'downplay')); + + function onHoverLink(method) { + var visualMapModel = this.visualMapModel; + + visualMapModel.option.hoverLink && this.api.dispatchAction({ + type: method, + batch: visualMapModel.findTargetDataIndices(pieceIndex) + }); + } + }, + + /** + * @private + */ + _getItemAlign: function () { + var visualMapModel = this.visualMapModel; + var modelOption = visualMapModel.option; + + if (modelOption.orient === 'vertical') { + return helper.getItemAlign( + visualMapModel, this.api, visualMapModel.itemSize + ); + } + else { // horizontal, most case left unless specifying right. + var align = modelOption.align; + if (!align || align === 'auto') { + align = 'left'; + } + return align; + } + }, + + /** + * @private + */ + _renderEndsText: function (group, text, itemSize) { + if (!text) { + return; + } + + var itemGroup = new graphic.Group(); + var textStyleModel = this.visualMapModel.textStyleModel; + + itemGroup.add(new graphic.Text({ + style: { + x: itemSize[0] / 2, + y: itemSize[1] / 2, + textVerticalAlign: 'middle', + textAlign: 'center', + text: text, + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + })); + + group.add(itemGroup); + }, + + /** + * @private + * @return {Object} {peiceList, endsText} The order is the same as screen pixel order. + */ + _getViewData: function () { + var visualMapModel = this.visualMapModel; + + var viewPieceList = zrUtil.map(visualMapModel.getPieceList(), function (piece, index) { + return {piece: piece, indexInModelPieceList: index}; + }); + var endsText = visualMapModel.get('text'); + + // Consider orient and inverse. + var orient = visualMapModel.get('orient'); + var inverse = visualMapModel.get('inverse'); + + // Order of model pieceList is always [low, ..., high] + if (orient === 'horizontal' ? inverse : !inverse) { + viewPieceList.reverse(); + } + // Origin order of endsText is [high, low] + else if (endsText) { + endsText = endsText.slice().reverse(); + } + + return {viewPieceList: viewPieceList, endsText: endsText}; + }, + + /** + * @private + */ + _createItemSymbol: function (group, representValue, shapeParam) { + group.add(symbolCreators.createSymbol( + this.getControllerVisual(representValue, 'symbol'), + shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], + this.getControllerVisual(representValue, 'color') + )); + }, + + /** + * @private + */ + _onItemClick: function (piece) { + var visualMapModel = this.visualMapModel; + var option = visualMapModel.option; + var selected = zrUtil.clone(option.selected); + var newKey = visualMapModel.getSelectedMapKey(piece); + + if (option.selectedMode === 'single') { + selected[newKey] = true; + zrUtil.each(selected, function (o, key) { + selected[key] = key === newKey; + }); + } + else { + selected[newKey] = !selected[newKey]; + } + + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: selected + }); + } + }); + + module.exports = PiecewiseVisualMapView; + + + +/***/ }, +/* 345 */ +/***/ function(module, exports, __webpack_require__) { + + // HINT Markpoint can't be used too much + + + __webpack_require__(346); + __webpack_require__(348); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markPoint component is enabled + opt.markPoint = opt.markPoint || {}; + }); + + +/***/ }, +/* 346 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markPoint', + + defaultOption: { + zlevel: 0, + z: 5, + symbol: 'pin', + symbolSize: 50, + //symbolRotate: 0, + //symbolOffset: [0, 0] + tooltip: { + trigger: 'item' + }, + label: { + normal: { + show: true, + position: 'inside' + }, + emphasis: { + show: true + } + }, + itemStyle: { + normal: { + borderWidth: 2 + } + } + } + }); + + +/***/ }, +/* 347 */ +/***/ function(module, exports, __webpack_require__) { + + + + var modelUtil = __webpack_require__(5); + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + + var formatUtil = __webpack_require__(6); + var addCommas = formatUtil.addCommas; + var encodeHTML = formatUtil.encodeHTML; + + function fillLabel(opt) { + modelUtil.defaultEmphasis( + opt.label, + modelUtil.LABEL_OPTIONS + ); + } + var MarkerModel = __webpack_require__(1).extendComponentModel({ + + type: 'marker', + + dependencies: ['series', 'grid', 'polar', 'geo'], + /** + * @overrite + */ + init: function (option, parentModel, ecModel, extraOpt) { + + if (true) { + if (this.type === 'marker') { + throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.'); + } + } + this.mergeDefaultAndTheme(option, ecModel); + this.mergeOption(option, ecModel, extraOpt.createdBySelf, true); + }, + + /** + * @return {boolean} + */ + ifEnableAnimation: function () { + if (env.node) { + return false; + } + + var hostSeries = this.__hostSeries; + return this.getShallow('animation') && hostSeries && hostSeries.ifEnableAnimation(); + }, + + mergeOption: function (newOpt, ecModel, createdBySelf, isInit) { + var MarkerModel = this.constructor; + var modelPropName = this.mainType + 'Model'; + if (!createdBySelf) { + ecModel.eachSeries(function (seriesModel) { + + var markerOpt = seriesModel.get(this.mainType); + + var markerModel = seriesModel[modelPropName]; + if (!markerOpt || !markerOpt.data) { + seriesModel[modelPropName] = null; + return; + } + if (!markerModel) { + if (isInit) { + // Default label emphasis `position` and `show` + fillLabel(markerOpt); + } + zrUtil.each(markerOpt.data, function (item) { + // FIXME Overwrite fillLabel method ? + if (item instanceof Array) { + fillLabel(item[0]); + fillLabel(item[1]); + } + else { + fillLabel(item); + } + }); + var opt = { + mainType: this.mainType, + // Use the same series index and name + seriesIndex: seriesModel.seriesIndex, + name: seriesModel.name, + createdBySelf: true + }; + markerModel = new MarkerModel( + markerOpt, this, ecModel, opt + ); + markerModel.__hostSeries = seriesModel; + } + else { + markerModel.mergeOption(markerOpt, ecModel, true); + } + seriesModel[modelPropName] = markerModel; + }, this); + } + }, + + formatTooltip: function (dataIndex) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? zrUtil.map(value, addCommas).join(', ') : addCommas(value); + var name = data.getName(dataIndex); + var html = this.name; + if (value != null || name) { + html += '
'; + } + if (name) { + html += encodeHTML(name); + if (value != null) { + html += ' : '; + } + } + if (value != null) { + html += formattedValue; + } + return html; + }, + + getData: function () { + return this._data; + }, + + setData: function (data) { + this._data = data; + } + }); + + zrUtil.mixin(MarkerModel, modelUtil.dataFormatMixin); + + module.exports = MarkerModel; + + +/***/ }, +/* 348 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var List = __webpack_require__(97); + + var markerHelper = __webpack_require__(349); + + function updateMarkerLayout(mpData, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var point; + var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + // Chart like bar may have there own marker positioning logic + else if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + mpData.getValues(mpData.dimensions, idx) + ); + } + else if (coordSys) { + var x = mpData.get(coordSys.dimensions[0], idx); + var y = mpData.get(coordSys.dimensions[1], idx); + point = coordSys.dataToPoint([x, y]); + + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + + mpData.setItemLayout(idx, point); + }); + } + + __webpack_require__(350).extend({ + + type: 'markPoint', + + updateLayout: function (markPointModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mpModel = seriesModel.markPointModel; + if (mpModel) { + updateMarkerLayout(mpModel.getData(), seriesModel, api); + this.markerGroupMap[seriesModel.name].updateLayout(mpModel); + } + }, this); + }, + + renderSeries: function (seriesModel, mpModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var symbolDrawMap = this.markerGroupMap; + var symbolDraw = symbolDrawMap[seriesName]; + if (!symbolDraw) { + symbolDraw = symbolDrawMap[seriesName] = new SymbolDraw(); + } + + var mpData = createList(coordSys, seriesModel, mpModel); + + // FIXME + mpModel.setData(mpData); + + updateMarkerLayout(mpModel.getData(), seriesModel, api); + + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var symbolSize = itemModel.getShallow('symbolSize'); + if (typeof symbolSize === 'function') { + // FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据? + symbolSize = symbolSize( + mpModel.getRawValue(idx), mpModel.getDataParams(idx) + ); + } + mpData.setItemVisual(idx, { + symbolSize: symbolSize, + color: itemModel.get('itemStyle.normal.color') + || seriesData.getVisual('color'), + symbol: itemModel.getShallow('symbol') + }); + }); + + // TODO Text are wrong + symbolDraw.updateData(mpData); + this.group.add(symbolDraw.group); + + // Set host model for tooltip + // FIXME + mpData.eachItemGraphicEl(function (el) { + el.traverse(function (child) { + child.dataModel = mpModel; + }); + }); + + symbolDraw.__keep = true; + + symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} [coordSys] + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, mpModel) { + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + } + + var mpData = new List(coordDimsInfos, mpModel); + var dataOpt = zrUtil.map(mpModel.get('data'), zrUtil.curry( + markerHelper.dataTransform, seriesModel + )); + if (coordSys) { + dataOpt = zrUtil.filter( + dataOpt, zrUtil.curry(markerHelper.dataFilter, coordSys) + ); + } + + mpData.initData(dataOpt, null, + coordSys ? markerHelper.dimValueGetter : function (item) { + return item.value; + } + ); + return mpData; + } + + + +/***/ }, +/* 349 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var indexOf = zrUtil.indexOf; + + function hasXOrY(item) { + return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y))); + } + + function hasXAndY(item) { + return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y)); + } + + function getPrecision(data, valueAxisDim, dataIndex) { + var precision = -1; + do { + precision = Math.max( + numberUtil.getPrecision(data.get( + valueAxisDim, dataIndex + )), + precision + ); + data = data.stackedOn; + } while (data); + + return precision; + } + + function markerTypeCalculatorWithExtent( + mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex + ) { + var coordArr = []; + var value = numCalculate(data, targetDataDim, mlType); + + var dataIndex = data.indexOfNearest(targetDataDim, value, true); + coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex, true); + coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex, true); + + var precision = getPrecision(data, targetDataDim, dataIndex); + if (precision >= 0) { + coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision); + } + + return coordArr; + } + + var curry = zrUtil.curry; + // TODO Specified percent + var markerTypeCalculator = { + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + min: curry(markerTypeCalculatorWithExtent, 'min'), + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + max: curry(markerTypeCalculatorWithExtent, 'max'), + + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + average: curry(markerTypeCalculatorWithExtent, 'average') + }; + + /** + * Transform markPoint data item to format used in List by do the following + * 1. Calculate statistic like `max`, `min`, `average` + * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/coord/*} [coordSys] + * @param {Object} item + * @return {Object} + */ + var dataTransform = function (seriesModel, item) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + + // 1. If not specify the position with pixel directly + // 2. If `coord` is not a data array. Which uses `xAxis`, + // `yAxis` to specify the coord on each dimension + + // parseFloat first because item.x and item.y can be percent string like '20%' + if (item && !hasXAndY(item) && !zrUtil.isArray(item.coord) && coordSys) { + var dims = coordSys.dimensions; + var axisInfo = getAxisInfo(item, data, coordSys, seriesModel); + + // Clone the option + // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value + item = zrUtil.clone(item); + + if (item.type + && markerTypeCalculator[item.type] + && axisInfo.baseAxis && axisInfo.valueAxis + ) { + var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim); + var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim); + + item.coord = markerTypeCalculator[item.type]( + data, axisInfo.baseDataDim, axisInfo.valueDataDim, + otherCoordIndex, targetCoordIndex + ); + // Force to use the value of calculated value. + item.value = item.coord[targetCoordIndex]; + } + else { + // FIXME Only has one of xAxis and yAxis. + var coord = [ + item.xAxis != null ? item.xAxis : item.radiusAxis, + item.yAxis != null ? item.yAxis : item.angleAxis + ]; + // Each coord support max, min, average + for (var i = 0; i < 2; i++) { + if (markerTypeCalculator[coord[i]]) { + var dataDim = seriesModel.coordDimToDataDim(dims[i])[0]; + coord[i] = numCalculate(data, dataDim, coord[i]); + } + } + item.coord = coord; + } + } + return item; + }; + + var getAxisInfo = function (item, data, coordSys, seriesModel) { + var ret = {}; + + if (item.valueIndex != null || item.valueDim != null) { + ret.valueDataDim = item.valueIndex != null + ? data.getDimension(item.valueIndex) : item.valueDim; + ret.valueAxis = coordSys.getAxis(seriesModel.dataDimToCoordDim(ret.valueDataDim)); + ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis); + ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0]; + } + else { + ret.baseAxis = seriesModel.getBaseAxis(); + ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis); + ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0]; + ret.valueDataDim = seriesModel.coordDimToDataDim(ret.valueAxis.dim)[0]; + } + + return ret; + }; + + /** + * Filter data which is out of coordinateSystem range + * [dataFilter description] + * @param {module:echarts/coord/*} [coordSys] + * @param {Object} item + * @return {boolean} + */ + var dataFilter = function (coordSys, item) { + // Alwalys return true if there is no coordSys + return (coordSys && coordSys.containData && item.coord && !hasXOrY(item)) + ? coordSys.containData(item.coord) : true; + }; + + var dimValueGetter = function (item, dimName, dataIndex, dimIndex) { + // x, y, radius, angle + if (dimIndex < 2) { + return item.coord && item.coord[dimIndex]; + } + return item.value; + }; + + var numCalculate = function (data, valueDataDim, type) { + if (type === 'average') { + var sum = 0; + var count = 0; + data.each(valueDataDim, function (val, idx) { + if (!isNaN(val)) { + sum += val; + count++; + } + }, true); + return sum / count; + } + else { + return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0]; + } + }; + + module.exports = { + dataTransform: dataTransform, + dataFilter: dataFilter, + dimValueGetter: dimValueGetter, + getAxisInfo: getAxisInfo, + numCalculate: numCalculate + }; + + +/***/ }, +/* 350 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'marker', + + init: function () { + /** + * Markline grouped by series + * @private + * @type {Object} + */ + this.markerGroupMap = {}; + }, + + render: function (markerModel, ecModel, api) { + var markerGroupMap = this.markerGroupMap; + for (var name in markerGroupMap) { + markerGroupMap[name].__keep = false; + } + + var markerModelKey = this.type + 'Model'; + ecModel.eachSeries(function (seriesModel) { + var markerModel = seriesModel[markerModelKey]; + markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api); + }, this); + + for (var name in markerGroupMap) { + if (!markerGroupMap[name].__keep) { + this.group.remove(markerGroupMap[name].group); + } + } + }, + + renderSeries: function () {} + }); + + +/***/ }, +/* 351 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(352); + __webpack_require__(353); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markLine component is enabled + opt.markLine = opt.markLine || {}; + }); + + +/***/ }, +/* 352 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markLine', + + defaultOption: { + zlevel: 0, + z: 5, + + symbol: ['circle', 'arrow'], + symbolSize: [8, 16], + + //symbolRotate: 0, + + precision: 2, + tooltip: { + trigger: 'item' + }, + label: { + normal: { + show: true, + position: 'end' + }, + emphasis: { + show: true + } + }, + lineStyle: { + normal: { + type: 'dashed' + }, + emphasis: { + width: 3 + } + }, + animationEasing: 'linear' + } + }); + + +/***/ }, +/* 353 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var List = __webpack_require__(97); + var numberUtil = __webpack_require__(7); + + var markerHelper = __webpack_require__(349); + + var LineDraw = __webpack_require__(199); + + var markLineTransform = function (seriesModel, coordSys, mlModel, item) { + var data = seriesModel.getData(); + // Special type markLine like 'min', 'max', 'average' + var mlType = item.type; + + if (!zrUtil.isArray(item) + && ( + mlType === 'min' || mlType === 'max' || mlType === 'average' + // In case + // data: [{ + // yAxis: 10 + // }] + || (item.xAxis != null || item.yAxis != null) + ) + ) { + var valueAxis; + var valueDataDim; + var value; + + if (item.yAxis != null || item.xAxis != null) { + valueDataDim = item.yAxis != null ? 'y' : 'x'; + valueAxis = coordSys.getAxis(valueDataDim); + + value = zrUtil.retrieve(item.yAxis, item.xAxis); + } + else { + var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel); + valueDataDim = axisInfo.valueDataDim; + valueAxis = axisInfo.valueAxis; + value = markerHelper.numCalculate(data, valueDataDim, mlType); + } + var valueIndex = valueDataDim === 'x' ? 0 : 1; + var baseIndex = 1 - valueIndex; + + var mlFrom = zrUtil.clone(item); + var mlTo = {}; + + mlFrom.type = null; + + mlFrom.coord = []; + mlTo.coord = []; + mlFrom.coord[baseIndex] = -Infinity; + mlTo.coord[baseIndex] = Infinity; + + var precision = mlModel.get('precision'); + if (precision >= 0) { + value = +value.toFixed(precision); + } + + mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value; + + item = [mlFrom, mlTo, { // Extra option for tooltip and label + type: mlType, + valueIndex: item.valueIndex, + // Force to use the value of calculated value. + value: value + }]; + } + + item = [ + markerHelper.dataTransform(seriesModel, item[0]), + markerHelper.dataTransform(seriesModel, item[1]), + zrUtil.extend({}, item[2]) + ]; + + // Avoid line data type is extended by from(to) data type + item[2].type = item[2].type || ''; + + // Merge from option and to option into line option + zrUtil.merge(item[2], item[0]); + zrUtil.merge(item[2], item[1]); + + return item; + }; + + function isInifinity(val) { + return !isNaN(val) && !isFinite(val); + } + + // If a markLine has one dim + function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + var dimName = coordSys.dimensions[dimIndex]; + return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]) + && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]); + } + + function markLineFilter(coordSys, item) { + if (coordSys.type === 'cartesian2d') { + var fromCoord = item[0].coord; + var toCoord = item[1].coord; + // In case + // { + // markLine: { + // data: [{ yAxis: 2 }] + // } + // } + if ( + fromCoord && toCoord && + (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) + || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys)) + ) { + return true; + } + } + return markerHelper.dataFilter(coordSys, item[0]) + && markerHelper.dataFilter(coordSys, item[1]); + } + + function updateSingleMarkerEndLayout( + data, idx, isFrom, seriesModel, api + ) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + + var point; + var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + data.getValues(data.dimensions, idx) + ); + } + else { + var dims = coordSys.dimensions; + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + point = coordSys.dataToPoint([x, y]); + } + // Expand line to the edge of grid if value on one axis is Inifnity + // In case + // markLine: { + // data: [{ + // yAxis: 2 + // // or + // type: 'average' + // }] + // } + if (coordSys.type === 'cartesian2d') { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var dims = coordSys.dimensions; + if (isInifinity(data.get(dims[0], idx))) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]); + } + else if (isInifinity(data.get(dims[1], idx))) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]); + } + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + + data.setItemLayout(idx, point); + } + + __webpack_require__(350).extend({ + + type: 'markLine', + + updateLayout: function (markLineModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mlModel = seriesModel.markLineModel; + if (mlModel) { + var mlData = mlModel.getData(); + var fromData = mlModel.__from; + var toData = mlModel.__to; + // Update visual and layout of from symbol and to symbol + fromData.each(function (idx) { + updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api); + updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api); + }); + // Update layout of line + mlData.each(function (idx) { + mlData.setItemLayout(idx, [ + fromData.getItemLayout(idx), + toData.getItemLayout(idx) + ]); + }); + + this.markerGroupMap[seriesModel.name].updateLayout(); + + } + }, this); + }, + + renderSeries: function (seriesModel, mlModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var lineDrawMap = this.markerGroupMap; + var lineDraw = lineDrawMap[seriesName]; + if (!lineDraw) { + lineDraw = lineDrawMap[seriesName] = new LineDraw(); + } + this.group.add(lineDraw.group); + + var mlData = createList(coordSys, seriesModel, mlModel); + + var fromData = mlData.from; + var toData = mlData.to; + var lineData = mlData.line; + + mlModel.__from = fromData; + mlModel.__to = toData; + // Line data for tooltip and formatter + mlModel.setData(lineData); + + var symbolType = mlModel.get('symbol'); + var symbolSize = mlModel.get('symbolSize'); + if (!zrUtil.isArray(symbolType)) { + symbolType = [symbolType, symbolType]; + } + if (typeof symbolSize === 'number') { + symbolSize = [symbolSize, symbolSize]; + } + + // Update visual and layout of from symbol and to symbol + mlData.from.each(function (idx) { + updateDataVisualAndLayout(fromData, idx, true); + updateDataVisualAndLayout(toData, idx, false); + }); + + // Update visual and layout of line + lineData.each(function (idx) { + var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color'); + lineData.setItemVisual(idx, { + color: lineColor || fromData.getItemVisual(idx, 'color') + }); + lineData.setItemLayout(idx, [ + fromData.getItemLayout(idx), + toData.getItemLayout(idx) + ]); + + lineData.setItemVisual(idx, { + 'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'), + 'fromSymbol': fromData.getItemVisual(idx, 'symbol'), + 'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'), + 'toSymbol': toData.getItemVisual(idx, 'symbol') + }); + }); + + lineDraw.updateData(lineData); + + // Set host model for tooltip + // FIXME + mlData.line.eachItemGraphicEl(function (el, idx) { + el.traverse(function (child) { + child.dataModel = mlModel; + }); + }); + + function updateDataVisualAndLayout(data, idx, isFrom) { + var itemModel = data.getItemModel(idx); + + updateSingleMarkerEndLayout( + data, idx, isFrom, seriesModel, api + ); + + data.setItemVisual(idx, { + symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1], + symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1], + color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color') + }); + } + + lineDraw.__keep = true; + + lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} coordSys + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, mlModel) { + + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + } + + var fromData = new List(coordDimsInfos, mlModel); + var toData = new List(coordDimsInfos, mlModel); + // No dimensions + var lineData = new List([], mlModel); + + var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry( + markLineTransform, seriesModel, coordSys, mlModel + )); + if (coordSys) { + optData = zrUtil.filter( + optData, zrUtil.curry(markLineFilter, coordSys) + ); + } + var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) { + return item.value; + }; + fromData.initData( + zrUtil.map(optData, function (item) { return item[0]; }), + null, dimValueGetter + ); + toData.initData( + zrUtil.map(optData, function (item) { return item[1]; }), + null, dimValueGetter + ); + lineData.initData( + zrUtil.map(optData, function (item) { return item[2]; }) + ); + lineData.hasItemOption = true; + return { + from: fromData, + to: toData, + line: lineData + }; + } + + +/***/ }, +/* 354 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(355); + __webpack_require__(356); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markArea component is enabled + opt.markArea = opt.markArea || {}; + }); + + +/***/ }, +/* 355 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markArea', + + defaultOption: { + zlevel: 0, + // PENDING + z: 1, + tooltip: { + trigger: 'item' + }, + // markArea should fixed on the coordinate system + animation: false, + label: { + normal: { + show: true, + position: 'top' + }, + emphasis: { + show: true, + position: 'top' + } + }, + itemStyle: { + normal: { + // color and borderColor default to use color from series + // color: 'auto' + // borderColor: 'auto' + borderWidth: 0 + } + } + } + }); + + +/***/ }, +/* 356 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Better on polar + + + var zrUtil = __webpack_require__(4); + var List = __webpack_require__(97); + var numberUtil = __webpack_require__(7); + var graphic = __webpack_require__(43); + var colorUtil = __webpack_require__(39); + + var markerHelper = __webpack_require__(349); + + var markAreaTransform = function (seriesModel, coordSys, maModel, item) { + var lt = markerHelper.dataTransform(seriesModel, item[0]); + var rb = markerHelper.dataTransform(seriesModel, item[1]); + var retrieve = zrUtil.retrieve; + + // FIXME make sure lt is less than rb + var ltCoord = lt.coord; + var rbCoord = rb.coord; + ltCoord[0] = retrieve(ltCoord[0], -Infinity); + ltCoord[1] = retrieve(ltCoord[1], -Infinity); + + rbCoord[0] = retrieve(rbCoord[0], Infinity); + rbCoord[1] = retrieve(rbCoord[1], Infinity); + + // Merge option into one + var result = zrUtil.mergeAll([{}, lt, rb]); + + result.coord = [ + lt.coord, rb.coord + ]; + result.x0 = lt.x; + result.y0 = lt.y; + result.x1 = rb.x; + result.y1 = rb.y; + return result; + }; + + function isInifinity(val) { + return !isNaN(val) && !isFinite(val); + } + + // If a markArea has one dim + function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]); + } + + function markAreaFilter(coordSys, item) { + var fromCoord = item.coord[0]; + var toCoord = item.coord[1]; + if (coordSys.type === 'cartesian2d') { + // In case + // { + // markArea: { + // data: [{ yAxis: 2 }] + // } + // } + if ( + fromCoord && toCoord && + (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) + || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys)) + ) { + return true; + } + } + return markerHelper.dataFilter(coordSys, { + coord: fromCoord, + x: item.x0, + y: item.y0 + }) + || markerHelper.dataFilter(coordSys, { + coord: toCoord, + x: item.x1, + y: item.y1 + }); + } + + // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0'] + function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + + var point; + var xPx = numberUtil.parsePercent(itemModel.get(dims[0]), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get(dims[1]), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + data.getValues(dims, idx) + ); + } + else { + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + point = coordSys.dataToPoint([x, y], true); + } + if (coordSys.type === 'cartesian2d') { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + if (isInifinity(x)) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]); + } + else if (isInifinity(y)) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]); + } + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + + return point; + } + + var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']]; + + __webpack_require__(350).extend({ + + type: 'markArea', + + updateLayout: function (markAreaModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var maModel = seriesModel.markAreaModel; + if (maModel) { + var areaData = maModel.getData(); + areaData.each(function (idx) { + var points = zrUtil.map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); + }); + // Layout + areaData.setItemLayout(idx, points); + var el = areaData.getItemGraphicEl(idx); + el.setShape('points', points); + }); + } + }, this); + }, + + renderSeries: function (seriesModel, maModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var areaGroupMap = this.markerGroupMap; + var polygonGroup = areaGroupMap[seriesName]; + if (!polygonGroup) { + polygonGroup = areaGroupMap[seriesName] = { + group: new graphic.Group() + }; + } + this.group.add(polygonGroup.group); + polygonGroup.__keep = true; + + var areaData = createList(coordSys, seriesModel, maModel); + + // Line data for tooltip and formatter + maModel.setData(areaData); + + // Update visual and layout of line + areaData.each(function (idx) { + // Layout + areaData.setItemLayout(idx, zrUtil.map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); + })); + + // Visual + areaData.setItemVisual(idx, { + color: seriesData.getVisual('color') + }); + }); + + + areaData.diff(polygonGroup.__data) + .add(function (idx) { + var polygon = new graphic.Polygon({ + shape: { + points: areaData.getItemLayout(idx) + } + }); + areaData.setItemGraphicEl(idx, polygon); + polygonGroup.group.add(polygon); + }) + .update(function (newIdx, oldIdx) { + var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx); + graphic.updateProps(polygon, { + shape: { + points: areaData.getItemLayout(newIdx) + } + }, maModel, newIdx); + polygonGroup.group.add(polygon); + areaData.setItemGraphicEl(newIdx, polygon); + }) + .remove(function (idx) { + var polygon = polygonGroup.__data.getItemGraphicEl(idx); + polygonGroup.group.remove(polygon); + }) + .execute(); + + areaData.eachItemGraphicEl(function (polygon, idx) { + var itemModel = areaData.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var color = areaData.getItemVisual(idx, 'color'); + polygon.useStyle( + zrUtil.defaults( + itemModel.getModel('itemStyle.normal').getItemStyle(), + { + fill: colorUtil.modifyAlpha(color, 0.4), + stroke: color + } + ) + ); + + polygon.hoverStyle = itemModel.getModel('itemStyle.normal').getItemStyle(); + + var defaultValue = areaData.getName(idx) || ''; + var textColor = color || polygon.style.fill; + graphic.setText(polygon.style, labelModel, textColor); + polygon.style.text = zrUtil.retrieve( + maModel.getFormattedLabel(idx, 'normal'), + defaultValue + ); + + graphic.setText(polygon.hoverStyle, labelHoverModel, textColor); + polygon.hoverStyle.text = zrUtil.retrieve( + maModel.getFormattedLabel(idx, 'emphasis'), + defaultValue + ); + + graphic.setHoverStyle(polygon, {}); + + polygon.dataModel = maModel; + }); + + polygonGroup.__data = areaData; + + polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} coordSys + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, maModel) { + + var coordDimsInfos; + var areaData; + var dims = ['x0', 'y0', 'x1', 'y1']; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + areaData = new List(zrUtil.map(dims, function (dim, idx) { + return { + name: dim, + type: coordDimsInfos[idx % 2].type + }; + }), maModel); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + areaData = new List(coordDimsInfos, maModel); + } + + var optData = zrUtil.map(maModel.get('data'), zrUtil.curry( + markAreaTransform, seriesModel, coordSys, maModel + )); + if (coordSys) { + optData = zrUtil.filter( + optData, zrUtil.curry(markAreaFilter, coordSys) + ); + } + + var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) { + return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2]; + } : function (item) { + return item.value; + }; + areaData.initData(optData, null, dimValueGetter); + areaData.hasItemOption = true; + return areaData; + } + + +/***/ }, +/* 357 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + var echarts = __webpack_require__(1); + + echarts.registerPreprocessor(__webpack_require__(358)); + + __webpack_require__(359); + __webpack_require__(360); + __webpack_require__(361); + __webpack_require__(363); + + + +/***/ }, +/* 358 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline preprocessor + */ + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + var timelineOpt = option && option.timeline; + + if (!zrUtil.isArray(timelineOpt)) { + timelineOpt = timelineOpt ? [timelineOpt] : []; + } + + zrUtil.each(timelineOpt, function (opt) { + if (!opt) { + return; + } + + compatibleEC2(opt); + }); + }; + + function compatibleEC2(opt) { + var type = opt.type; + + var ec2Types = {'number': 'value', 'time': 'time'}; + + // Compatible with ec2 + if (ec2Types[type]) { + opt.axisType = ec2Types[type]; + delete opt.type; + } + + transferItem(opt); + + if (has(opt, 'controlPosition')) { + var controlStyle = opt.controlStyle || (opt.controlStyle = {}); + if (!has(controlStyle, 'position')) { + controlStyle.position = opt.controlPosition; + } + if (controlStyle.position === 'none' && !has(controlStyle, 'show')) { + controlStyle.show = false; + delete controlStyle.position; + } + delete opt.controlPosition; + } + + zrUtil.each(opt.data || [], function (dataItem) { + if (zrUtil.isObject(dataItem) && !zrUtil.isArray(dataItem)) { + if (!has(dataItem, 'value') && has(dataItem, 'name')) { + // In ec2, using name as value. + dataItem.value = dataItem.name; + } + transferItem(dataItem); + } + }); + } + + function transferItem(opt) { + var itemStyle = opt.itemStyle || (opt.itemStyle = {}); + + var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); + + // Transfer label out + var label = opt.label || (opt.label || {}); + var labelNormal = label.normal || (label.normal = {}); + var excludeLabelAttr = {normal: 1, emphasis: 1}; + + zrUtil.each(label, function (value, name) { + if (!excludeLabelAttr[name] && !has(labelNormal, name)) { + labelNormal[name] = value; + } + }); + + if (itemStyleEmphasis.label && !has(label, 'emphasis')) { + label.emphasis = itemStyleEmphasis.label; + delete itemStyleEmphasis.label; + } + } + + function has(obj, attr) { + return obj.hasOwnProperty(attr); + } + + + +/***/ }, +/* 359 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('timeline', function () { + // Only slider now. + return 'slider'; + }); + + + +/***/ }, +/* 360 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeilne action + */ + + + var echarts = __webpack_require__(1); + + echarts.registerAction( + + {type: 'timelineChange', event: 'timelineChanged', update: 'prepareAndUpdate'}, + + function (payload, ecModel) { + + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.currentIndex != null) { + timelineModel.setCurrentIndex(payload.currentIndex); + + if (!timelineModel.get('loop', true) && timelineModel.isIndexMax()) { + timelineModel.setPlayState(false); + } + } + + ecModel.resetOption('timeline'); + } + ); + + echarts.registerAction( + + {type: 'timelinePlayChange', event: 'timelinePlayChanged', update: 'update'}, + + function (payload, ecModel) { + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.playState != null) { + timelineModel.setPlayState(payload.playState); + } + } + ); + + + +/***/ }, +/* 361 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Silder timeline model + */ + + + var TimelineModel = __webpack_require__(362); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + var SliderTimelineModel = TimelineModel.extend({ + + type: 'timeline.slider', + + /** + * @protected + */ + defaultOption: { + + backgroundColor: 'rgba(0,0,0,0)', // 时间轴背景颜色 + borderColor: '#ccc', // 时间轴边框颜色 + borderWidth: 0, // 时间轴边框线宽,单位px,默认为0(无边框) + + orient: 'horizontal', // 'vertical' + inverse: false, + + tooltip: { // boolean or Object + trigger: 'item' // data item may also have tootip attr. + }, + + symbol: 'emptyCircle', + symbolSize: 10, + + lineStyle: { + show: true, + width: 2, + color: '#304654' + }, + label: { // 文本标签 + position: 'auto', // auto left right top bottom + // When using number, label position is not + // restricted by viewRect. + // positive: right/bottom, negative: left/top + normal: { + show: true, + interval: 'auto', + rotate: 0, + // formatter: null, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#304654' + } + }, + emphasis: { + show: true, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#c23531' + } + } + }, + itemStyle: { + normal: { + color: '#304654', + borderWidth: 1 + }, + emphasis: { + color: '#c23531' + } + }, + + checkpointStyle: { + symbol: 'circle', + symbolSize: 13, + color: '#c23531', + borderWidth: 5, + borderColor: 'rgba(194,53,49, 0.5)', + animation: true, + animationDuration: 300, + animationEasing: 'quinticInOut' + }, + + controlStyle: { + show: true, + showPlayBtn: true, + showPrevBtn: true, + showNextBtn: true, + itemSize: 22, + itemGap: 12, + position: 'left', // 'left' 'right' 'top' 'bottom' + playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z', // jshint ignore:line + stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z', // jshint ignore:line + nextIcon: 'path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z', // jshint ignore:line + prevIcon: 'path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z', // jshint ignore:line + normal: { + color: '#304654', + borderColor: '#304654', + borderWidth: 1 + }, + emphasis: { + color: '#c23531', + borderColor: '#c23531', + borderWidth: 2 + } + }, + data: [] + } + + }); + + zrUtil.mixin(SliderTimelineModel, modelUtil.dataFormatMixin); + + module.exports = SliderTimelineModel; + + +/***/ }, +/* 362 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline model + */ + + + var ComponentModel = __webpack_require__(19); + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + var TimelineModel = ComponentModel.extend({ + + type: 'timeline', + + layoutMode: 'box', + + /** + * @protected + */ + defaultOption: { + + zlevel: 0, // 一级层叠 + z: 4, // 二级层叠 + show: true, + + axisType: 'time', // 模式是时间类型,支持 value, category + + realtime: true, + + left: '20%', + top: null, + right: '20%', + bottom: 0, + width: null, + height: 40, + padding: 5, + + controlPosition: 'left', // 'left' 'right' 'top' 'bottom' 'none' + autoPlay: false, + rewind: false, // 反向播放 + loop: true, + playInterval: 2000, // 播放时间间隔,单位ms + + currentIndex: 0, + + itemStyle: { + normal: {}, + emphasis: {} + }, + label: { + normal: { + textStyle: { + color: '#000' + } + }, + emphasis: {} + }, + + data: [] + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel) { + + /** + * @private + * @type {module:echarts/data/List} + */ + this._data; + + /** + * @private + * @type {Array.} + */ + this._names; + + this.mergeDefaultAndTheme(option, ecModel); + this._initData(); + }, + + /** + * @override + */ + mergeOption: function (option) { + TimelineModel.superApply(this, 'mergeOption', arguments); + this._initData(); + }, + + /** + * @param {number} [currentIndex] + */ + setCurrentIndex: function (currentIndex) { + if (currentIndex == null) { + currentIndex = this.option.currentIndex; + } + var count = this._data.count(); + + if (this.option.loop) { + currentIndex = (currentIndex % count + count) % count; + } + else { + currentIndex >= count && (currentIndex = count - 1); + currentIndex < 0 && (currentIndex = 0); + } + + this.option.currentIndex = currentIndex; + }, + + /** + * @return {number} currentIndex + */ + getCurrentIndex: function () { + return this.option.currentIndex; + }, + + /** + * @return {boolean} + */ + isIndexMax: function () { + return this.getCurrentIndex() >= this._data.count() - 1; + }, + + /** + * @param {boolean} state true: play, false: stop + */ + setPlayState: function (state) { + this.option.autoPlay = !!state; + }, + + /** + * @return {boolean} true: play, false: stop + */ + getPlayState: function () { + return !!this.option.autoPlay; + }, + + /** + * @private + */ + _initData: function () { + var thisOption = this.option; + var dataArr = thisOption.data || []; + var axisType = thisOption.axisType; + var names = this._names = []; + + if (axisType === 'category') { + var idxArr = []; + zrUtil.each(dataArr, function (item, index) { + var value = modelUtil.getDataItemValue(item); + var newItem; + + if (zrUtil.isObject(item)) { + newItem = zrUtil.clone(item); + newItem.value = index; + } + else { + newItem = index; + } + + idxArr.push(newItem); + + if (!zrUtil.isString(value) && (value == null || isNaN(value))) { + value = ''; + } + + names.push(value + ''); + }); + dataArr = idxArr; + } + + var dimType = ({category: 'ordinal', time: 'time'})[axisType] || 'number'; + + var data = this._data = new List([{name: 'value', type: dimType}], this); + + data.initData(dataArr, names); + }, + + getData: function () { + return this._data; + }, + + /** + * @public + * @return {Array.} categoreis + */ + getCategories: function () { + if (this.get('axisType') === 'category') { + return this._names.slice(); + } + } + + }); + + module.exports = TimelineModel; + + +/***/ }, +/* 363 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Silder timeline view + */ + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + var TimelineView = __webpack_require__(364); + var TimelineAxis = __webpack_require__(365); + var symbolUtil = __webpack_require__(106); + var axisHelper = __webpack_require__(114); + var BoundingRect = __webpack_require__(9); + var matrix = __webpack_require__(11); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + + var bind = zrUtil.bind; + var each = zrUtil.each; + + var PI = Math.PI; + + module.exports = TimelineView.extend({ + + type: 'timeline.slider', + + init: function (ecModel, api) { + + this.api = api; + + /** + * @private + * @type {module:echarts/component/timeline/TimelineAxis} + */ + this._axis; + + /** + * @private + * @type {module:zrender/core/BoundingRect} + */ + this._viewRect; + + /** + * @type {number} + */ + this._timer; + + /** + * @type {module:zrende/Element} + */ + this._currentPointer; + + /** + * @type {module:zrender/container/Group} + */ + this._mainGroup; + + /** + * @type {module:zrender/container/Group} + */ + this._labelGroup; + }, + + /** + * @override + */ + render: function (timelineModel, ecModel, api, payload) { + this.model = timelineModel; + this.api = api; + this.ecModel = ecModel; + + this.group.removeAll(); + + if (timelineModel.get('show', true)) { + + var layoutInfo = this._layout(timelineModel, api); + var mainGroup = this._createGroup('mainGroup'); + var labelGroup = this._createGroup('labelGroup'); + + /** + * @private + * @type {module:echarts/component/timeline/TimelineAxis} + */ + var axis = this._axis = this._createAxis(layoutInfo, timelineModel); + + timelineModel.formatTooltip = function (dataIndex) { + return encodeHTML(axis.scale.getLabel(dataIndex)); + }; + + each( + ['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], + function (name) { + this['_render' + name](layoutInfo, mainGroup, axis, timelineModel); + }, + this + ); + + this._renderAxisLabel(layoutInfo, labelGroup, axis, timelineModel); + + this._position(layoutInfo, timelineModel); + } + + this._doPlayStop(); + }, + + /** + * @override + */ + remove: function () { + this._clearTimer(); + this.group.removeAll(); + }, + + /** + * @override + */ + dispose: function () { + this._clearTimer(); + }, + + _layout: function (timelineModel, api) { + var labelPosOpt = timelineModel.get('label.normal.position'); + var orient = timelineModel.get('orient'); + var viewRect = getViewRect(timelineModel, api); + // Auto label offset. + if (labelPosOpt == null || labelPosOpt === 'auto') { + labelPosOpt = orient === 'horizontal' + ? ((viewRect.y + viewRect.height / 2) < api.getHeight() / 2 ? '-' : '+') + : ((viewRect.x + viewRect.width / 2) < api.getWidth() / 2 ? '+' : '-'); + } + else if (isNaN(labelPosOpt)) { + labelPosOpt = ({ + horizontal: {top: '-', bottom: '+'}, + vertical: {left: '-', right: '+'} + })[orient][labelPosOpt]; + } + + // FIXME + // 暂没有实现用户传入 + // var labelAlign = timelineModel.get('label.normal.textStyle.align'); + // var labelBaseline = timelineModel.get('label.normal.textStyle.baseline'); + var labelAlignMap = { + horizontal: 'center', + vertical: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'left' : 'right' + }; + + var labelBaselineMap = { + horizontal: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'top' : 'bottom', + vertical: 'middle' + }; + var rotationMap = { + horizontal: 0, + vertical: PI / 2 + }; + + // Position + var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width; + + var controlModel = timelineModel.getModel('controlStyle'); + var showControl = controlModel.get('show'); + var controlSize = showControl ? controlModel.get('itemSize') : 0; + var controlGap = showControl ? controlModel.get('itemGap') : 0; + var sizePlusGap = controlSize + controlGap; + + // Special label rotate. + var labelRotation = timelineModel.get('label.normal.rotate') || 0; + labelRotation = labelRotation * PI / 180; // To radian. + + var playPosition; + var prevBtnPosition; + var nextBtnPosition; + var axisExtent; + var controlPosition = controlModel.get('position', true); + var showControl = controlModel.get('show', true); + var showPlayBtn = showControl && controlModel.get('showPlayBtn', true); + var showPrevBtn = showControl && controlModel.get('showPrevBtn', true); + var showNextBtn = showControl && controlModel.get('showNextBtn', true); + var xLeft = 0; + var xRight = mainLength; + + // position[0] means left, position[1] means middle. + if (controlPosition === 'left' || controlPosition === 'bottom') { + showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap); + showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } + else { // 'top' 'right' + showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } + axisExtent = [xLeft, xRight]; + + if (timelineModel.get('inverse')) { + axisExtent.reverse(); + } + + return { + viewRect: viewRect, + mainLength: mainLength, + orient: orient, + + rotation: rotationMap[orient], + labelRotation: labelRotation, + labelPosOpt: labelPosOpt, + labelAlign: labelAlignMap[orient], + labelBaseline: labelBaselineMap[orient], + + // Based on mainGroup. + playPosition: playPosition, + prevBtnPosition: prevBtnPosition, + nextBtnPosition: nextBtnPosition, + axisExtent: axisExtent, + + controlSize: controlSize, + controlGap: controlGap + }; + }, + + _position: function (layoutInfo, timelineModel) { + // Position is be called finally, because bounding rect is needed for + // adapt content to fill viewRect (auto adapt offset). + + // Timeline may be not all in the viewRect when 'offset' is specified + // as a number, because it is more appropriate that label aligns at + // 'offset' but not the other edge defined by viewRect. + + var mainGroup = this._mainGroup; + var labelGroup = this._labelGroup; + + var viewRect = layoutInfo.viewRect; + if (layoutInfo.orient === 'vertical') { + // transfrom to horizontal, inverse rotate by left-top point. + var m = matrix.create(); + var rotateOriginX = viewRect.x; + var rotateOriginY = viewRect.y + viewRect.height; + matrix.translate(m, m, [-rotateOriginX, -rotateOriginY]); + matrix.rotate(m, m, -PI / 2); + matrix.translate(m, m, [rotateOriginX, rotateOriginY]); + viewRect = viewRect.clone(); + viewRect.applyTransform(m); + } + + var viewBound = getBound(viewRect); + var mainBound = getBound(mainGroup.getBoundingRect()); + var labelBound = getBound(labelGroup.getBoundingRect()); + + var mainPosition = mainGroup.position; + var labelsPosition = labelGroup.position; + + labelsPosition[0] = mainPosition[0] = viewBound[0][0]; + + var labelPosOpt = layoutInfo.labelPosOpt; + + if (isNaN(labelPosOpt)) { // '+' or '-' + var mainBoundIdx = labelPosOpt === '+' ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx); + } + else { + var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + labelsPosition[1] = mainPosition[1] + labelPosOpt; + } + + mainGroup.attr('position', mainPosition); + labelGroup.attr('position', labelsPosition); + mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation; + + setOrigin(mainGroup); + setOrigin(labelGroup); + + function setOrigin(targetGroup) { + var pos = targetGroup.position; + targetGroup.origin = [ + viewBound[0][0] - pos[0], + viewBound[1][0] - pos[1] + ]; + } + + function getBound(rect) { + // [[xmin, xmax], [ymin, ymax]] + return [ + [rect.x, rect.x + rect.width], + [rect.y, rect.y + rect.height] + ]; + } + + function toBound(fromPos, from, to, dimIdx, boundIdx) { + fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx]; + } + }, + + _createAxis: function (layoutInfo, timelineModel) { + var data = timelineModel.getData(); + var axisType = timelineModel.get('axisType'); + + var scale = axisHelper.createScaleByModel(timelineModel, axisType); + var dataExtent = data.getDataExtent('value'); + scale.setExtent(dataExtent[0], dataExtent[1]); + this._customizeScale(scale, data); + scale.niceTicks(); + + var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType); + axis.model = timelineModel; + + return axis; + }, + + _customizeScale: function (scale, data) { + + scale.getTicks = function () { + return data.mapArray(['value'], function (value) { + return value; + }); + }; + + scale.getTicksLabels = function () { + return zrUtil.map(this.getTicks(), scale.getLabel, scale); + }; + }, + + _createGroup: function (name) { + var newGroup = this['_' + name] = new graphic.Group(); + this.group.add(newGroup); + return newGroup; + }, + + _renderAxisLine: function (layoutInfo, group, axis, timelineModel) { + var axisExtent = axis.getExtent(); + + if (!timelineModel.get('lineStyle.show')) { + return; + } + + group.add(new graphic.Line({ + shape: { + x1: axisExtent[0], y1: 0, + x2: axisExtent[1], y2: 0 + }, + style: zrUtil.extend( + {lineCap: 'round'}, + timelineModel.getModel('lineStyle').getLineStyle() + ), + silent: true, + z2: 1 + })); + }, + + /** + * @private + */ + _renderAxisTick: function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + var ticks = axis.scale.getTicks(); + + each(ticks, function (value, dataIndex) { + + var tickCoord = axis.dataToCoord(value); + var itemModel = data.getItemModel(dataIndex); + var itemStyleModel = itemModel.getModel('itemStyle.normal'); + var hoverStyleModel = itemModel.getModel('itemStyle.emphasis'); + var symbolOpt = { + position: [tickCoord, 0], + onclick: bind(this._changeTimeline, this, dataIndex) + }; + var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt); + graphic.setHoverStyle(el, hoverStyleModel.getItemStyle()); + + if (itemModel.get('tooltip')) { + el.dataIndex = dataIndex; + el.dataModel = timelineModel; + } + else { + el.dataIndex = el.dataModel = null; + } + + }, this); + }, + + /** + * @private + */ + _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) { + var labelModel = timelineModel.getModel('label.normal'); + + if (!labelModel.get('show')) { + return; + } + + var data = timelineModel.getData(); + var ticks = axis.scale.getTicks(); + var labels = axisHelper.getFormattedLabels( + axis, labelModel.get('formatter') + ); + var labelInterval = axis.getLabelInterval(); + + each(ticks, function (tick, dataIndex) { + if (axis.isLabelIgnored(dataIndex, labelInterval)) { + return; + } + + var itemModel = data.getItemModel(dataIndex); + var itemTextStyleModel = itemModel.getModel('label.normal.textStyle'); + var hoverTextStyleModel = itemModel.getModel('label.emphasis.textStyle'); + var tickCoord = axis.dataToCoord(tick); + var textEl = new graphic.Text({ + style: { + text: labels[dataIndex], + textAlign: layoutInfo.labelAlign, + textVerticalAlign: layoutInfo.labelBaseline, + textFont: itemTextStyleModel.getFont(), + fill: itemTextStyleModel.getTextColor() + }, + position: [tickCoord, 0], + rotation: layoutInfo.labelRotation - layoutInfo.rotation, + onclick: bind(this._changeTimeline, this, dataIndex), + silent: false + }); + + group.add(textEl); + graphic.setHoverStyle(textEl, hoverTextStyleModel.getItemStyle()); + + }, this); + }, + + /** + * @private + */ + _renderControl: function (layoutInfo, group, axis, timelineModel) { + var controlSize = layoutInfo.controlSize; + var rotation = layoutInfo.rotation; + + var itemStyle = timelineModel.getModel('controlStyle.normal').getItemStyle(); + var hoverStyle = timelineModel.getModel('controlStyle.emphasis').getItemStyle(); + var rect = [0, -controlSize / 2, controlSize, controlSize]; + var playState = timelineModel.getPlayState(); + var inverse = timelineModel.get('inverse', true); + + makeBtn( + layoutInfo.nextBtnPosition, + 'controlStyle.nextIcon', + bind(this._changeTimeline, this, inverse ? '-' : '+') + ); + makeBtn( + layoutInfo.prevBtnPosition, + 'controlStyle.prevIcon', + bind(this._changeTimeline, this, inverse ? '+' : '-') + ); + makeBtn( + layoutInfo.playPosition, + 'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'), + bind(this._handlePlayClick, this, !playState), + true + ); + + function makeBtn(position, iconPath, onclick, willRotate) { + if (!position) { + return; + } + var opt = { + position: position, + origin: [controlSize / 2, 0], + rotation: willRotate ? -rotation : 0, + rectHover: true, + style: itemStyle, + onclick: onclick + }; + var btn = makeIcon(timelineModel, iconPath, rect, opt); + group.add(btn); + graphic.setHoverStyle(btn, hoverStyle); + } + }, + + _renderCurrentPointer: function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + var currentIndex = timelineModel.getCurrentIndex(); + var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle'); + var me = this; + + var callback = { + onCreate: function (pointer) { + pointer.draggable = true; + pointer.drift = bind(me._handlePointerDrag, me); + pointer.ondragend = bind(me._handlePointerDragend, me); + pointerMoveTo(pointer, currentIndex, axis, timelineModel, true); + }, + onUpdate: function (pointer) { + pointerMoveTo(pointer, currentIndex, axis, timelineModel); + } + }; + + // Reuse when exists, for animation and drag. + this._currentPointer = giveSymbol( + pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback + ); + }, + + _handlePlayClick: function (nextState) { + this._clearTimer(); + this.api.dispatchAction({ + type: 'timelinePlayChange', + playState: nextState, + from: this.uid + }); + }, + + _handlePointerDrag: function (dx, dy, e) { + this._clearTimer(); + this._pointerChangeTimeline([e.offsetX, e.offsetY]); + }, + + _handlePointerDragend: function (e) { + this._pointerChangeTimeline([e.offsetX, e.offsetY], true); + }, + + _pointerChangeTimeline: function (mousePos, trigger) { + var toCoord = this._toAxisCoord(mousePos)[0]; + + var axis = this._axis; + var axisExtent = numberUtil.asc(axis.getExtent().slice()); + + toCoord > axisExtent[1] && (toCoord = axisExtent[1]); + toCoord < axisExtent[0] && (toCoord = axisExtent[0]); + + this._currentPointer.position[0] = toCoord; + this._currentPointer.dirty(); + + var targetDataIndex = this._findNearestTick(toCoord); + var timelineModel = this.model; + + if (trigger || ( + targetDataIndex !== timelineModel.getCurrentIndex() + && timelineModel.get('realtime') + )) { + this._changeTimeline(targetDataIndex); + } + }, + + _doPlayStop: function () { + this._clearTimer(); + + if (this.model.getPlayState()) { + this._timer = setTimeout( + bind(handleFrame, this), + this.model.get('playInterval') + ); + } + + function handleFrame() { + // Do not cache + var timelineModel = this.model; + this._changeTimeline( + timelineModel.getCurrentIndex() + + (timelineModel.get('rewind', true) ? -1 : 1) + ); + } + }, + + _toAxisCoord: function (vertex) { + var trans = this._mainGroup.getLocalTransform(); + return graphic.applyTransform(vertex, trans, true); + }, + + _findNearestTick: function (axisCoord) { + var data = this.model.getData(); + var dist = Infinity; + var targetDataIndex; + var axis = this._axis; + + data.each(['value'], function (value, dataIndex) { + var coord = axis.dataToCoord(value); + var d = Math.abs(coord - axisCoord); + if (d < dist) { + dist = d; + targetDataIndex = dataIndex; + } + }); + + return targetDataIndex; + }, + + _clearTimer: function () { + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + }, + + _changeTimeline: function (nextIndex) { + var currentIndex = this.model.getCurrentIndex(); + + if (nextIndex === '+') { + nextIndex = currentIndex + 1; + } + else if (nextIndex === '-') { + nextIndex = currentIndex - 1; + } + + this.api.dispatchAction({ + type: 'timelineChange', + currentIndex: nextIndex, + from: this.uid + }); + } + + }); + + function getViewRect(model, api) { + return layout.getLayoutRect( + model.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + }, + model.get('padding') + ); + } + + function makeIcon(timelineModel, objPath, rect, opts) { + var icon = graphic.makePath( + timelineModel.get(objPath).replace(/^path:\/\//, ''), + zrUtil.clone(opts || {}), + new BoundingRect(rect[0], rect[1], rect[2], rect[3]), + 'center' + ); + + return icon; + } + + /** + * Create symbol or update symbol + */ + function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) { + var symbolType = hostModel.get('symbol'); + var color = itemStyleModel.get('color'); + var symbolSize = hostModel.get('symbolSize'); + var halfSymbolSize = symbolSize / 2; + var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']); + + if (!symbol) { + symbol = symbolUtil.createSymbol( + symbolType, -halfSymbolSize, -halfSymbolSize, symbolSize, symbolSize, color + ); + group.add(symbol); + callback && callback.onCreate(symbol); + } + else { + symbol.setStyle(itemStyle); + symbol.setColor(color); + group.add(symbol); // Group may be new, also need to add. + callback && callback.onUpdate(symbol); + } + + opt = zrUtil.merge({ + rectHover: true, + style: itemStyle, + z2: 100 + }, opt, true); + + symbol.attr(opt); + + return symbol; + } + + function pointerMoveTo(pointer, dataIndex, axis, timelineModel, noAnimation) { + if (pointer.dragging) { + return; + } + + var pointerModel = timelineModel.getModel('checkpointStyle'); + var toCoord = axis.dataToCoord(timelineModel.getData().get(['value'], dataIndex)); + + if (noAnimation || !pointerModel.get('animation', true)) { + pointer.attr({position: [toCoord, 0]}); + } + else { + pointer.stopAnimation(true); + pointer.animateTo( + {position: [toCoord, 0]}, + pointerModel.get('animationDuration', true), + pointerModel.get('animationEasing', true) + ); + } + } + + + +/***/ }, +/* 364 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline view + */ + + + // var zrUtil = require('zrender/lib/core/util'); + // var graphic = require('../../util/graphic'); + var ComponentView = __webpack_require__(29); + + module.exports = ComponentView.extend({ + + type: 'timeline' + }); + + + +/***/ }, +/* 365 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisHelper = __webpack_require__(114); + + /** + * Extend axis 2d + * @constructor module:echarts/coord/cartesian/Axis2D + * @extends {module:echarts/coord/cartesian/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var TimelineAxis = function (dim, scale, coordExtent, axisType) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * @private + * @type {number} + */ + this._autoLabelInterval; + + /** + * Axis model + * @param {module:echarts/component/TimelineModel} + */ + this.model = null; + }; + + TimelineAxis.prototype = { + + constructor: TimelineAxis, + + /** + * @public + * @return {number} + */ + getLabelInterval: function () { + var timelineModel = this.model; + var labelModel = timelineModel.getModel('label.normal'); + var labelInterval = labelModel.get('interval'); + + if (labelInterval != null && labelInterval != 'auto') { + return labelInterval; + } + + var labelInterval = this._autoLabelInterval; + + if (!labelInterval) { + labelInterval = this._autoLabelInterval = axisHelper.getAxisLabelInterval( + zrUtil.map(this.scale.getTicks(), this.dataToCoord, this), + axisHelper.getFormattedLabels(this, labelModel.get('formatter')), + labelModel.getModel('textStyle').getFont(), + timelineModel.get('orient') === 'horizontal' + ); + } + + return labelInterval; + }, + + /** + * If label is ignored. + * Automatically used when axis is category and label can not be all shown + * @public + * @param {number} idx + * @return {boolean} + */ + isLabelIgnored: function (idx) { + if (this.type === 'category') { + var labelInterval = this.getLabelInterval(); + return ((typeof labelInterval === 'function') + && !labelInterval(idx, this.scale.getLabel(idx))) + || idx % (labelInterval + 1); + } + } + + }; + + zrUtil.inherits(TimelineAxis, Axis); + + module.exports = TimelineAxis; + + +/***/ }, +/* 366 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(367); + __webpack_require__(368); + + __webpack_require__(370); + __webpack_require__(371); + __webpack_require__(372); + __webpack_require__(373); + __webpack_require__(378); + + +/***/ }, +/* 367 */ +/***/ function(module, exports, __webpack_require__) { + + + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + + var ToolboxModel = __webpack_require__(1).extendComponentModel({ + + type: 'toolbox', + + layoutMode: { + type: 'box', + ignoreSize: true + }, + + mergeDefaultAndTheme: function (option) { + ToolboxModel.superApply(this, 'mergeDefaultAndTheme', arguments); + + zrUtil.each(this.option.feature, function (featureOpt, featureName) { + var Feature = featureManager.get(featureName); + Feature && zrUtil.merge(featureOpt, Feature.defaultOption); + }); + }, + + defaultOption: { + + show: true, + + z: 6, + + zlevel: 0, + + orient: 'horizontal', + + left: 'right', + + top: 'top', + + // right + // bottom + + backgroundColor: 'transparent', + + borderColor: '#ccc', + + borderWidth: 0, + + padding: 5, + + itemSize: 15, + + itemGap: 8, + + showTitle: true, + + iconStyle: { + normal: { + borderColor: '#666', + color: 'none' + }, + emphasis: { + borderColor: '#3E98C5' + } + } + // textStyle: {}, + + // feature + } + }); + + module.exports = ToolboxModel; + + +/***/ }, +/* 368 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) { + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + var DataDiffer = __webpack_require__(98); + var listComponentHelper = __webpack_require__(277); + var textContain = __webpack_require__(8); + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'toolbox', + + render: function (toolboxModel, ecModel, api, payload) { + var group = this.group; + group.removeAll(); + + if (!toolboxModel.get('show')) { + return; + } + + var itemSize = +toolboxModel.get('itemSize'); + var featureOpts = toolboxModel.get('feature') || {}; + var features = this._features || (this._features = {}); + + var featureNames = []; + zrUtil.each(featureOpts, function (opt, name) { + featureNames.push(name); + }); + + (new DataDiffer(this._featureNames || [], featureNames)) + .add(process) + .update(process) + .remove(zrUtil.curry(process, null)) + .execute(); + + // Keep for diff. + this._featureNames = featureNames; + + function process(newIndex, oldIndex) { + var featureName = featureNames[newIndex]; + var oldName = featureNames[oldIndex]; + var featureOpt = featureOpts[featureName]; + var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel); + var feature; + + if (featureName && !oldName) { // Create + if (isUserFeatureName(featureName)) { + feature = { + model: featureModel, + onclick: featureModel.option.onclick, + featureName: featureName + }; + } + else { + var Feature = featureManager.get(featureName); + if (!Feature) { + return; + } + feature = new Feature(featureModel, ecModel, api); + } + features[featureName] = feature; + } + else { + feature = features[oldName]; + // If feature does not exsit. + if (!feature) { + return; + } + feature.model = featureModel; + feature.ecModel = ecModel; + feature.api = api; + } + + if (!featureName && oldName) { + feature.dispose && feature.dispose(ecModel, api); + return; + } + + if (!featureModel.get('show') || feature.unusable) { + feature.remove && feature.remove(ecModel, api); + return; + } + + createIconPaths(featureModel, feature, featureName); + + featureModel.setIconStatus = function (iconName, status) { + var option = this.option; + var iconPaths = this.iconPaths; + option.iconStatus = option.iconStatus || {}; + option.iconStatus[iconName] = status; + // FIXME + iconPaths[iconName] && iconPaths[iconName].trigger(status); + }; + + if (feature.render) { + feature.render(featureModel, ecModel, api, payload); + } + } + + function createIconPaths(featureModel, feature, featureName) { + var iconStyleModel = featureModel.getModel('iconStyle'); + + // If one feature has mutiple icon. they are orginaized as + // { + // icon: { + // foo: '', + // bar: '' + // }, + // title: { + // foo: '', + // bar: '' + // } + // } + var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon'); + var titles = featureModel.get('title') || {}; + if (typeof icons === 'string') { + var icon = icons; + var title = titles; + icons = {}; + titles = {}; + icons[featureName] = icon; + titles[featureName] = title; + } + var iconPaths = featureModel.iconPaths = {}; + zrUtil.each(icons, function (icon, iconName) { + var normalStyle = iconStyleModel.getModel('normal').getItemStyle(); + var hoverStyle = iconStyleModel.getModel('emphasis').getItemStyle(); + + var style = { + x: -itemSize / 2, + y: -itemSize / 2, + width: itemSize, + height: itemSize + }; + var path = icon.indexOf('image://') === 0 + ? ( + style.image = icon.slice(8), + new graphic.Image({style: style}) + ) + : graphic.makePath( + icon.replace('path://', ''), + { + style: normalStyle, + hoverStyle: hoverStyle, + rectHover: true + }, + style, + 'center' + ); + + graphic.setHoverStyle(path); + + if (toolboxModel.get('showTitle')) { + path.__title = titles[iconName]; + path.on('mouseover', function () { + path.setStyle({ + text: titles[iconName], + textPosition: hoverStyle.textPosition || 'bottom', + textFill: hoverStyle.fill || hoverStyle.stroke || '#000', + textAlign: hoverStyle.textAlign || 'center' + }); + }) + .on('mouseout', function () { + path.setStyle({ + textFill: null + }); + }); + } + path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal'); + + group.add(path); + path.on('click', zrUtil.bind( + feature.onclick, feature, ecModel, api, iconName + )); + + iconPaths[iconName] = path; + }); + } + + listComponentHelper.layout(group, toolboxModel, api); + // Render background after group is layout + // FIXME + listComponentHelper.addBackground(group, toolboxModel); + + // Adjust icon title positions to avoid them out of screen + group.eachChild(function (icon) { + var titleText = icon.__title; + var hoverStyle = icon.hoverStyle; + // May be background element + if (hoverStyle && titleText) { + var rect = textContain.getBoundingRect( + titleText, hoverStyle.font + ); + var offsetX = icon.position[0] + group.position[0]; + var offsetY = icon.position[1] + group.position[1] + itemSize; + + var needPutOnTop = false; + if (offsetY + rect.height > api.getHeight()) { + hoverStyle.textPosition = 'top'; + needPutOnTop = true; + } + var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8); + if (offsetX + rect.width / 2 > api.getWidth()) { + hoverStyle.textPosition = ['100%', topOffset]; + hoverStyle.textAlign = 'right'; + } + else if (offsetX - rect.width / 2 < 0) { + hoverStyle.textPosition = [0, topOffset]; + hoverStyle.textAlign = 'left'; + } + } + }); + }, + + updateView: function (toolboxModel, ecModel, api, payload) { + zrUtil.each(this._features, function (feature) { + feature.updateView && feature.updateView(feature.model, ecModel, api, payload); + }); + }, + + updateLayout: function (toolboxModel, ecModel, api, payload) { + zrUtil.each(this._features, function (feature) { + feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload); + }); + }, + + remove: function (ecModel, api) { + zrUtil.each(this._features, function (feature) { + feature.remove && feature.remove(ecModel, api); + }); + this.group.removeAll(); + }, + + dispose: function (ecModel, api) { + zrUtil.each(this._features, function (feature) { + feature.dispose && feature.dispose(ecModel, api); + }); + } + }); + + function isUserFeatureName(featureName) { + return featureName.indexOf('my') === 0; + } + + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(369))) + +/***/ }, +/* 369 */ +/***/ function(module, exports) { + + // shim for using process in browser + + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + (function () { + try { + cachedSetTimeout = setTimeout; + } catch (e) { + cachedSetTimeout = function () { + throw new Error('setTimeout is not defined'); + } + } + try { + cachedClearTimeout = clearTimeout; + } catch (e) { + cachedClearTimeout = function () { + throw new Error('clearTimeout is not defined'); + } + } + } ()) + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = cachedSetTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + cachedClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + cachedSetTimeout(drainQueue, 0); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 370 */ +/***/ function(module, exports, __webpack_require__) { + + + + var env = __webpack_require__(2); + + function SaveAsImage (model) { + this.model = model; + } + + SaveAsImage.defaultOption = { + show: true, + icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0', + title: '保存为图片', + type: 'png', + // Default use option.backgroundColor + // backgroundColor: '#fff', + name: '', + excludeComponents: ['toolbox'], + pixelRatio: 1, + lang: ['右键另存为图片'] + }; + + SaveAsImage.prototype.unusable = !env.canvasSupported; + + var proto = SaveAsImage.prototype; + + proto.onclick = function (ecModel, api) { + var model = this.model; + var title = model.get('name') || ecModel.get('title.0.text') || 'echarts'; + var $a = document.createElement('a'); + var type = model.get('type', true) || 'png'; + $a.download = title + '.' + type; + $a.target = '_blank'; + var url = api.getConnectedDataURL({ + type: type, + backgroundColor: model.get('backgroundColor', true) + || ecModel.get('backgroundColor') || '#fff', + excludeComponents: model.get('excludeComponents'), + pixelRatio: model.get('pixelRatio') + }); + $a.href = url; + // Chrome and Firefox + if (typeof MouseEvent === 'function') { + var evt = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: false + }); + $a.dispatchEvent(evt); + } + // IE + else { + var lang = model.get('lang'); + var html = '' + + '' + + '' + + ''; + var tab = window.open(); + tab.document.write(html); + } + }; + + __webpack_require__(314).register( + 'saveAsImage', SaveAsImage + ); + + module.exports = SaveAsImage; + + +/***/ }, +/* 371 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + function MagicType(model) { + this.model = model; + } + + MagicType.defaultOption = { + show: true, + type: [], + // Icon group + icon: { + line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4', + bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7', + stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line + tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z' + }, + title: { + line: '切换为折线图', + bar: '切换为柱状图', + stack: '切换为堆叠', + tiled: '切换为平铺' + }, + option: {}, + seriesIndex: {} + }; + + var proto = MagicType.prototype; + + proto.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon'); + var icons = {}; + zrUtil.each(model.get('type'), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + + var seriesOptGenreator = { + 'line': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + type: 'line', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get('option.line') || {}, true); + } + }, + 'bar': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line') { + return zrUtil.merge({ + id: seriesId, + type: 'bar', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get('option.bar') || {}, true); + } + }, + 'stack': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line' || seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + stack: '__ec_magicType_stack__' + }, model.get('option.stack') || {}, true); + } + }, + 'tiled': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line' || seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + stack: '' + }, model.get('option.tiled') || {}, true); + } + } + }; + + var radioTypes = [ + ['line', 'bar'], + ['stack', 'tiled'] + ]; + + proto.onclick = function (ecModel, api, type) { + var model = this.model; + var seriesIndex = model.get('seriesIndex.' + type); + // Not supported magicType + if (!seriesOptGenreator[type]) { + return; + } + var newOption = { + series: [] + }; + var generateNewSeriesTypes = function (seriesModel) { + var seriesType = seriesModel.subType; + var seriesId = seriesModel.id; + var newSeriesOpt = seriesOptGenreator[type]( + seriesType, seriesId, seriesModel, model + ); + if (newSeriesOpt) { + // PENDING If merge original option? + zrUtil.defaults(newSeriesOpt, seriesModel.option); + newOption.series.push(newSeriesOpt); + } + // Modify boundaryGap + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) { + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + if (categoryAxis) { + var axisDim = categoryAxis.dim; + var axisType = axisDim + 'Axis'; + var axisModel = ecModel.queryComponents({ + mainType: axisType, + index: seriesModel.get(name + 'Index'), + id: seriesModel.get(name + 'Id') + })[0]; + var axisIndex = axisModel.componentIndex; + + newOption[axisType] = newOption[axisType] || []; + for (var i = 0; i <= axisIndex; i++) { + newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {}; + } + newOption[axisType][axisIndex].boundaryGap = type === 'bar' ? true : false; + } + } + }; + + zrUtil.each(radioTypes, function (radio) { + if (zrUtil.indexOf(radio, type) >= 0) { + zrUtil.each(radio, function (item) { + model.setIconStatus(item, 'normal'); + }); + } + }); + + model.setIconStatus(type, 'emphasis'); + + ecModel.eachComponent( + { + mainType: 'series', + query: seriesIndex == null ? null : { + seriesIndex: seriesIndex + } + }, generateNewSeriesTypes + ); + api.dispatchAction({ + type: 'changeMagicType', + currentType: type, + newOption: newOption + }); + }; + + var echarts = __webpack_require__(1); + echarts.registerAction({ + type: 'changeMagicType', + event: 'magicTypeChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + ecModel.mergeOption(payload.newOption); + }); + + __webpack_require__(314).register('magicType', MagicType); + + module.exports = MagicType; + + +/***/ }, +/* 372 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/toolbox/feature/DataView + */ + + + + var zrUtil = __webpack_require__(4); + var eventTool = __webpack_require__(87); + + + var BLOCK_SPLITER = new Array(60).join('-'); + var ITEM_SPLITER = '\t'; + /** + * Group series into two types + * 1. on category axis, like line, bar + * 2. others, like scatter, pie + * @param {module:echarts/model/Global} ecModel + * @return {Object} + * @inner + */ + function groupSeries(ecModel) { + var seriesGroupByCategoryAxis = {}; + var otherSeries = []; + var meta = []; + ecModel.eachRawSeries(function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + + if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) { + var baseAxis = coordSys.getBaseAxis(); + if (baseAxis.type === 'category') { + var key = baseAxis.dim + '_' + baseAxis.index; + if (!seriesGroupByCategoryAxis[key]) { + seriesGroupByCategoryAxis[key] = { + categoryAxis: baseAxis, + valueAxis: coordSys.getOtherAxis(baseAxis), + series: [] + }; + meta.push({ + axisDim: baseAxis.dim, + axisIndex: baseAxis.index + }); + } + seriesGroupByCategoryAxis[key].series.push(seriesModel); + } + else { + otherSeries.push(seriesModel); + } + } + else { + otherSeries.push(seriesModel); + } + }); + + return { + seriesGroupByCategoryAxis: seriesGroupByCategoryAxis, + other: otherSeries, + meta: meta + }; + } + + /** + * Assemble content of series on cateogory axis + * @param {Array.} series + * @return {string} + * @inner + */ + function assembleSeriesWithCategoryAxis(series) { + var tables = []; + zrUtil.each(series, function (group, key) { + var categoryAxis = group.categoryAxis; + var valueAxis = group.valueAxis; + var valueAxisDim = valueAxis.dim; + + var headers = [' '].concat(zrUtil.map(group.series, function (series) { + return series.name; + })); + var columns = [categoryAxis.model.getCategories()]; + zrUtil.each(group.series, function (series) { + columns.push(series.getRawData().mapArray(valueAxisDim, function (val) { + return val; + })); + }); + // Assemble table content + var lines = [headers.join(ITEM_SPLITER)]; + for (var i = 0; i < columns[0].length; i++) { + var items = []; + for (var j = 0; j < columns.length; j++) { + items.push(columns[j][i]); + } + lines.push(items.join(ITEM_SPLITER)); + } + tables.push(lines.join('\n')); + }); + return tables.join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + + /** + * Assemble content of other series + * @param {Array.} series + * @return {string} + * @inner + */ + function assembleOtherSeries(series) { + return zrUtil.map(series, function (series) { + var data = series.getRawData(); + var lines = [series.name]; + var vals = []; + data.each(data.dimensions, function () { + var argLen = arguments.length; + var dataIndex = arguments[argLen - 1]; + var name = data.getName(dataIndex); + for (var i = 0; i < argLen - 1; i++) { + vals[i] = arguments[i]; + } + lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER)); + }); + return lines.join('\n'); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + + /** + * @param {module:echarts/model/Global} + * @return {string} + * @inner + */ + function getContentFromModel(ecModel) { + + var result = groupSeries(ecModel); + + return { + value: zrUtil.filter([ + assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), + assembleOtherSeries(result.other) + ], function (str) { + return str.replace(/[\n\t\s]/g, ''); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'), + + meta: result.meta + }; + } + + + function trim(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + /** + * If a block is tsv format + */ + function isTSVFormat(block) { + // Simple method to find out if a block is tsv format + var firstLine = block.slice(0, block.indexOf('\n')); + if (firstLine.indexOf(ITEM_SPLITER) >= 0) { + return true; + } + } + + var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g'); + /** + * @param {string} tsv + * @return {Array.} + */ + function parseTSVContents(tsv) { + var tsvLines = tsv.split(/\n+/g); + var headers = trim(tsvLines.shift()).split(itemSplitRegex); + + var categories = []; + var series = zrUtil.map(headers, function (header) { + return { + name: header, + data: [] + }; + }); + for (var i = 0; i < tsvLines.length; i++) { + var items = trim(tsvLines[i]).split(itemSplitRegex); + categories.push(items.shift()); + for (var j = 0; j < items.length; j++) { + series[j] && (series[j].data[i] = items[j]); + } + } + return { + series: series, + categories: categories + }; + } + + /** + * @param {string} str + * @return {Array.} + * @inner + */ + function parseListContents(str) { + var lines = str.split(/\n+/g); + var seriesName = trim(lines.shift()); + + var data = []; + for (var i = 0; i < lines.length; i++) { + var items = trim(lines[i]).split(itemSplitRegex); + var name = ''; + var value; + var hasName = false; + if (isNaN(items[0])) { // First item is name + hasName = true; + name = items[0]; + items = items.slice(1); + data[i] = { + name: name, + value: [] + }; + value = data[i].value; + } + else { + value = data[i] = []; + } + for (var j = 0; j < items.length; j++) { + value.push(+items[j]); + } + if (value.length === 1) { + hasName ? (data[i].value = value[0]) : (data[i] = value[0]); + } + } + + return { + name: seriesName, + data: data + }; + } + + /** + * @param {string} str + * @param {Array.} blockMetaList + * @return {Object} + * @inner + */ + function parseContents(str, blockMetaList) { + var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g')); + var newOption = { + series: [] + }; + zrUtil.each(blocks, function (block, idx) { + if (isTSVFormat(block)) { + var result = parseTSVContents(block); + var blockMeta = blockMetaList[idx]; + var axisKey = blockMeta.axisDim + 'Axis'; + + if (blockMeta) { + newOption[axisKey] = newOption[axisKey] || []; + newOption[axisKey][blockMeta.axisIndex] = { + data: result.categories + }; + newOption.series = newOption.series.concat(result.series); + } + } + else { + var result = parseListContents(block); + newOption.series.push(result); + } + }); + return newOption; + } + + /** + * @alias {module:echarts/component/toolbox/feature/DataView} + * @constructor + * @param {module:echarts/model/Model} model + */ + function DataView(model) { + + this._dom = null; + + this.model = model; + } + + DataView.defaultOption = { + show: true, + readOnly: false, + optionToContent: null, + contentToOption: null, + + icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28', + title: '数据视图', + lang: ['数据视图', '关闭', '刷新'], + backgroundColor: '#fff', + textColor: '#000', + textareaColor: '#fff', + textareaBorderColor: '#333', + buttonColor: '#c23531', + buttonTextColor: '#fff' + }; + + DataView.prototype.onclick = function (ecModel, api) { + var container = api.getDom(); + var model = this.model; + if (this._dom) { + container.removeChild(this._dom); + } + var root = document.createElement('div'); + root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;'; + root.style.backgroundColor = model.get('backgroundColor') || '#fff'; + + // Create elements + var header = document.createElement('h4'); + var lang = model.get('lang') || []; + header.innerHTML = lang[0] || model.get('title'); + header.style.cssText = 'margin: 10px 20px;'; + header.style.color = model.get('textColor'); + + var viewMain = document.createElement('div'); + var textarea = document.createElement('textarea'); + viewMain.style.cssText = 'display:block;width:100%;overflow:hidden;'; + + var optionToContent = model.get('optionToContent'); + var contentToOption = model.get('contentToOption'); + var result = getContentFromModel(ecModel); + if (typeof optionToContent === 'function') { + var htmlOrDom = optionToContent(api.getOption()); + if (typeof htmlOrDom === 'string') { + viewMain.innerHTML = htmlOrDom; + } + else if (zrUtil.isDom(htmlOrDom)) { + viewMain.appendChild(htmlOrDom); + } + } + else { + // Use default textarea + viewMain.appendChild(textarea); + textarea.readOnly = model.get('readOnly'); + textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;'; + textarea.style.color = model.get('textColor'); + textarea.style.borderColor = model.get('textareaBorderColor'); + textarea.style.backgroundColor = model.get('textareaColor'); + textarea.value = result.value; + } + + var blockMetaList = result.meta; + + var buttonContainer = document.createElement('div'); + buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;'; + + var buttonStyle = 'float:right;margin-right:20px;border:none;' + + 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px'; + var closeButton = document.createElement('div'); + var refreshButton = document.createElement('div'); + + buttonStyle += ';background-color:' + model.get('buttonColor'); + buttonStyle += ';color:' + model.get('buttonTextColor'); + + var self = this; + + function close() { + container.removeChild(root); + self._dom = null; + } + eventTool.addEventListener(closeButton, 'click', close); + + eventTool.addEventListener(refreshButton, 'click', function () { + var newOption; + try { + if (typeof contentToOption === 'function') { + newOption = contentToOption(viewMain, api.getOption()); + } + else { + newOption = parseContents(textarea.value, blockMetaList); + } + } + catch (e) { + close(); + throw new Error('Data view format error ' + e); + } + if (newOption) { + api.dispatchAction({ + type: 'changeDataView', + newOption: newOption + }); + } + + close(); + }); + + closeButton.innerHTML = lang[1]; + refreshButton.innerHTML = lang[2]; + refreshButton.style.cssText = buttonStyle; + closeButton.style.cssText = buttonStyle; + + !model.get('readOnly') && buttonContainer.appendChild(refreshButton); + buttonContainer.appendChild(closeButton); + + // http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea + eventTool.addEventListener(textarea, 'keydown', function (e) { + if ((e.keyCode || e.which) === 9) { + // get caret position/selection + var val = this.value; + var start = this.selectionStart; + var end = this.selectionEnd; + + // set textarea value to: text before caret + tab + text after caret + this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end); + + // put caret at right position again + this.selectionStart = this.selectionEnd = start + 1; + + // prevent the focus lose + eventTool.stop(e); + } + }); + + root.appendChild(header); + root.appendChild(viewMain); + root.appendChild(buttonContainer); + + viewMain.style.height = (container.clientHeight - 80) + 'px'; + + container.appendChild(root); + this._dom = root; + }; + + DataView.prototype.remove = function (ecModel, api) { + this._dom && api.getDom().removeChild(this._dom); + }; + + DataView.prototype.dispose = function (ecModel, api) { + this.remove(ecModel, api); + }; + + /** + * @inner + */ + function tryMergeDataOption(newData, originalData) { + return zrUtil.map(newData, function (newVal, idx) { + var original = originalData && originalData[idx]; + if (zrUtil.isObject(original) && !zrUtil.isArray(original)) { + if (zrUtil.isObject(newVal) && !zrUtil.isArray(newVal)) { + newVal = newVal.value; + } + // Original data has option + return zrUtil.defaults({ + value: newVal + }, original); + } + else { + return newVal; + } + }); + } + + __webpack_require__(314).register('dataView', DataView); + + __webpack_require__(1).registerAction({ + type: 'changeDataView', + event: 'dataViewChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + var newSeriesOptList = []; + zrUtil.each(payload.newOption.series, function (seriesOpt) { + var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0]; + if (!seriesModel) { + // New created series + // Geuss the series type + newSeriesOptList.push(zrUtil.extend({ + // Default is scatter + type: 'scatter' + }, seriesOpt)); + } + else { + var originalData = seriesModel.get('data'); + newSeriesOptList.push({ + name: seriesOpt.name, + data: tryMergeDataOption(seriesOpt.data, originalData) + }); + } + }); + + ecModel.mergeOption(zrUtil.defaults({ + series: newSeriesOptList + }, payload.newOption)); + }); + + module.exports = DataView; + + +/***/ }, +/* 373 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var BrushController = __webpack_require__(233); + var brushHelper = __webpack_require__(309); + var history = __webpack_require__(374); + + var each = zrUtil.each; + + // Use dataZoomSelect + __webpack_require__(375); + + // Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId + var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_'; + + function DataZoom(model, ecModel, api) { + + /** + * @private + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)) + .mount(); + + /** + * @private + * @type {boolean} + */ + this._isZoomActive; + } + + DataZoom.defaultOption = { + show: true, + // Icon group + icon: { + zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1', + back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26' + }, + title: { + zoom: '区域缩放', + back: '区域缩放还原' + } + }; + + var proto = DataZoom.prototype; + + proto.render = function (featureModel, ecModel, api, payload) { + this.model = featureModel; + this.ecModel = ecModel; + this.api = api; + + updateZoomBtnStatus(featureModel, ecModel, this, payload); + updateBackBtnStatus(featureModel, ecModel); + }; + + proto.onclick = function (ecModel, api, type) { + handlers[type].call(this); + }; + + proto.remove = function (ecModel, api) { + this._brushController.unmount(); + }; + + proto.dispose = function (ecModel, api) { + this._brushController.dispose(); + }; + + /** + * @private + */ + var handlers = { + + zoom: function () { + var nextActive = !this._isZoomActive; + + this.api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'dataZoomSelect', + dataZoomSelectActive: nextActive + }); + }, + + back: function () { + this._dispatchZoomAction(history.pop(this.ecModel)); + } + }; + + /** + * @private + */ + proto._onBrush = function (areas, opt) { + if (!opt.isEnd || !areas.length) { + return; + } + var snapshot = {}; + var ecModel = this.ecModel; + + this._brushController.updateCovers([]); // remove cover + + var coordInfoList = brushHelper.makeCoordInfoList( + retrieveAxisSetting(this.model.option), ecModel + ); + var rangesCoordInfoList = []; + brushHelper.parseOutputRanges(areas, coordInfoList, ecModel, rangesCoordInfoList); + + var area = areas[0]; // dataZoom can not multiple area. + var coordInfo = rangesCoordInfoList[0]; + var coordRange = area.coordRange; + var brushType = area.brushType; + + if (coordInfo && coordRange) { + if (brushType === 'rect') { + setBatch('xAxis', coordRange[0], coordInfo); + setBatch('yAxis', coordRange[1], coordInfo); + } + else { + var axisNames = {lineX: 'xAxis', lineY: 'yAxis'}; + setBatch(axisNames[brushType], coordRange, coordInfo); + } + } + + history.push(ecModel, snapshot); + + this._dispatchZoomAction(snapshot); + + function setBatch(axisName, minMax, coordInfo) { + var dataZoomModel = findDataZoom(axisName, coordInfo[axisName], ecModel); + if (dataZoomModel) { + snapshot[dataZoomModel.id] = { + dataZoomId: dataZoomModel.id, + startValue: minMax[0], + endValue: minMax[1] + }; + } + } + + function findDataZoom(axisName, axisModel, ecModel) { + var dataZoomModel; + ecModel.eachComponent( + {mainType: 'dataZoom', subType: 'select'}, + function (dzModel, dataZoomIndex) { + var axisIndex = dzModel.get(axisName + 'Index'); + if (axisIndex != null + && ecModel.getComponent(axisName, axisIndex) === axisModel + ) { + dataZoomModel = dzModel; + } + } + ); + return dataZoomModel; + } + }; + + /** + * @private + */ + proto._dispatchZoomAction = function (snapshot) { + var batch = []; + + // Convert from hash map to array. + each(snapshot, function (batchItem, dataZoomId) { + batch.push(zrUtil.clone(batchItem)); + }); + + batch.length && this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + batch: batch + }); + }; + + function retrieveAxisSetting(option) { + var setting = {}; + // Compatible with previous setting: null => all axis, false => no axis. + zrUtil.each(['xAxisIndex', 'yAxisIndex'], function (name) { + setting[name] = option[name]; + setting[name] == null && (setting[name] = 'all'); + (setting[name] === false || setting[name] === 'none') && (setting[name] = []); + }); + return setting; + } + + function updateBackBtnStatus(featureModel, ecModel) { + featureModel.setIconStatus( + 'back', + history.count(ecModel) > 1 ? 'emphasis' : 'normal' + ); + } + + function updateZoomBtnStatus(featureModel, ecModel, view, payload) { + var zoomActive = view._isZoomActive; + + if (payload && payload.type === 'takeGlobalCursor') { + zoomActive = payload.key === 'dataZoomSelect' + ? payload.dataZoomSelectActive : false; + } + + view._isZoomActive = zoomActive; + + featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal'); + + var coordInfoList = brushHelper.makeCoordInfoList( + retrieveAxisSetting(featureModel.option), ecModel + ); + var brushType = (coordInfoList.xAxisHas && !coordInfoList.yAxisHas) + ? 'lineX' + : (!coordInfoList.xAxisHas && coordInfoList.yAxisHas) + ? 'lineY' + : 'rect'; + + view._brushController + .setPanels(brushHelper.makePanelOpts(coordInfoList)) + .enableBrush( + zoomActive + ? { + brushType: brushType, + brushStyle: { // FIXME user customized? + lineWidth: 0, + // stroke: '#333', + fill: 'rgba(0,0,0,0.2)' + } + } + : false + ); + } + + + __webpack_require__(314).register('dataZoom', DataZoom); + + + // Create special dataZoom option for select + __webpack_require__(1).registerPreprocessor(function (option) { + if (!option) { + return; + } + + var dataZoomOpts = option.dataZoom || (option.dataZoom = []); + if (!zrUtil.isArray(dataZoomOpts)) { + option.dataZoom = dataZoomOpts = [dataZoomOpts]; + } + + var toolboxOpt = option.toolbox; + if (toolboxOpt) { + // Assume there is only one toolbox + if (zrUtil.isArray(toolboxOpt)) { + toolboxOpt = toolboxOpt[0]; + } + + if (toolboxOpt && toolboxOpt.feature) { + var dataZoomOpt = toolboxOpt.feature.dataZoom; + addForAxis('xAxis', dataZoomOpt); + addForAxis('yAxis', dataZoomOpt); + } + } + + function addForAxis(axisName, dataZoomOpt) { + if (!dataZoomOpt) { + return; + } + + // Try not to modify model, because it is not merged yet. + var axisIndicesName = axisName + 'Index'; + var givenAxisIndices = dataZoomOpt[axisIndicesName]; + if (givenAxisIndices != null + && givenAxisIndices != 'all' + && !zrUtil.isArray(givenAxisIndices) + ) { + givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices]; + } + + forEachComponent(axisName, function (axisOpt, axisIndex) { + if (givenAxisIndices != null + && givenAxisIndices != 'all' + && zrUtil.indexOf(givenAxisIndices, axisIndex) === -1 + ) { + return; + } + var newOpt = { + type: 'select', + $fromToolbox: true, + // Id for merge mapping. + id: DATA_ZOOM_ID_BASE + axisName + axisIndex + }; + // FIXME + // Only support one axis now. + newOpt[axisIndicesName] = axisIndex; + dataZoomOpts.push(newOpt); + }); + } + + function forEachComponent(mainType, cb) { + var opts = option[mainType]; + if (!zrUtil.isArray(opts)) { + opts = opts ? [opts] : []; + } + each(opts, cb); + } + }); + + module.exports = DataZoom; + + +/***/ }, +/* 374 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file History manager. + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + var ATTR = '\0_ec_hist_store'; + + var history = { + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]} + */ + push: function (ecModel, newSnapshot) { + var store = giveStore(ecModel); + + // If previous dataZoom can not be found, + // complete an range with current range. + each(newSnapshot, function (batchItem, dataZoomId) { + var i = store.length - 1; + for (; i >= 0; i--) { + var snapshot = store[i]; + if (snapshot[dataZoomId]) { + break; + } + } + if (i < 0) { + // No origin range set, create one by current range. + var dataZoomModel = ecModel.queryComponents( + {mainType: 'dataZoom', subType: 'select', id: dataZoomId} + )[0]; + if (dataZoomModel) { + var percentRange = dataZoomModel.getPercentRange(); + store[0][dataZoomId] = { + dataZoomId: dataZoomId, + start: percentRange[0], + end: percentRange[1] + }; + } + } + }); + + store.push(newSnapshot); + }, + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @return {Object} snapshot + */ + pop: function (ecModel) { + var store = giveStore(ecModel); + var head = store[store.length - 1]; + store.length > 1 && store.pop(); + + // Find top for all dataZoom. + var snapshot = {}; + each(head, function (batchItem, dataZoomId) { + for (var i = store.length - 1; i >= 0; i--) { + var batchItem = store[i][dataZoomId]; + if (batchItem) { + snapshot[dataZoomId] = batchItem; + break; + } + } + }); + + return snapshot; + }, + + /** + * @public + */ + clear: function (ecModel) { + ecModel[ATTR] = null; + }, + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @return {number} records. always >= 1. + */ + count: function (ecModel) { + return giveStore(ecModel).length; + } + + }; + + /** + * [{key: dataZoomId, value: {dataZoomId, range}}, ...] + * History length of each dataZoom may be different. + * this._history[0] is used to store origin range. + * @type {Array.} + */ + function giveStore(ecModel) { + var store = ecModel[ATTR]; + if (!store) { + store = ecModel[ATTR] = [{}]; + } + return store; + } + + module.exports = history; + + + +/***/ }, +/* 375 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(317); + + __webpack_require__(318); + __webpack_require__(321); + + __webpack_require__(376); + __webpack_require__(377); + + __webpack_require__(328); + __webpack_require__(329); + + + +/***/ }, +/* 376 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var DataZoomModel = __webpack_require__(318); + + module.exports = DataZoomModel.extend({ + + type: 'dataZoom.select' + + }); + + + +/***/ }, +/* 377 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(321).extend({ + + type: 'dataZoom.select' + + }); + + + +/***/ }, +/* 378 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var history = __webpack_require__(374); + + function Restore(model) { + this.model = model; + } + + Restore.defaultOption = { + show: true, + icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5', + title: '还原' + }; + + var proto = Restore.prototype; + + proto.onclick = function (ecModel, api, type) { + history.clear(ecModel); + + api.dispatchAction({ + type: 'restore', + from: this.uid + }); + }; + + + __webpack_require__(314).register('restore', Restore); + + + __webpack_require__(1).registerAction( + {type: 'restore', event: 'restore', update: 'prepareAndUpdate'}, + function (payload, ecModel) { + ecModel.resetOption('recreate'); + } + ); + + module.exports = Restore; + + +/***/ }, +/* 379 */ +/***/ function(module, exports, __webpack_require__) { + + + __webpack_require__(380); + __webpack_require__(81).registerPainter('vml', __webpack_require__(382)); + + +/***/ }, +/* 380 */ +/***/ function(module, exports, __webpack_require__) { + + // http://www.w3.org/TR/NOTE-VML + // TODO Use proxy like svg instead of overwrite brush methods + + + if (!__webpack_require__(2).canvasSupported) { + var vec2 = __webpack_require__(10); + var BoundingRect = __webpack_require__(9); + var CMD = __webpack_require__(49).CMD; + var colorTool = __webpack_require__(39); + var textContain = __webpack_require__(8); + var RectText = __webpack_require__(48); + var Displayable = __webpack_require__(46); + var ZImage = __webpack_require__(62); + var Text = __webpack_require__(64); + var Path = __webpack_require__(45); + + var Gradient = __webpack_require__(61); + + var vmlCore = __webpack_require__(381); + + var round = Math.round; + var sqrt = Math.sqrt; + var abs = Math.abs; + var cos = Math.cos; + var sin = Math.sin; + var mathMax = Math.max; + + var applyTransform = vec2.applyTransform; + + var comma = ','; + var imageTransformPrefix = 'progid:DXImageTransform.Microsoft'; + + var Z = 21600; + var Z2 = Z / 2; + + var ZLEVEL_BASE = 100000; + var Z_BASE = 1000; + + var initRootElStyle = function (el) { + el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;'; + el.coordsize = Z + ',' + Z; + el.coordorigin = '0,0'; + }; + + var encodeHtmlAttribute = function (s) { + return String(s).replace(/&/g, '&').replace(/"/g, '"'); + }; + + var rgb2Str = function (r, g, b) { + return 'rgb(' + [r, g, b].join(',') + ')'; + }; + + var append = function (parent, child) { + if (child && parent && child.parentNode !== parent) { + parent.appendChild(child); + } + }; + + var remove = function (parent, child) { + if (child && parent && child.parentNode === parent) { + parent.removeChild(child); + } + }; + + var getZIndex = function (zlevel, z, z2) { + // z 的取值范围为 [0, 1000] + return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2; + }; + + var parsePercent = function (value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; + }; + + /*************************************************** + * PATH + **************************************************/ + + var setColorAndOpacity = function (el, color, opacity) { + var colorArr = colorTool.parse(color); + opacity = +opacity; + if (isNaN(opacity)) { + opacity = 1; + } + if (colorArr) { + el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]); + el.opacity = opacity * colorArr[3]; + } + }; + + var getColorAndAlpha = function (color) { + var colorArr = colorTool.parse(color); + return [ + rgb2Str(colorArr[0], colorArr[1], colorArr[2]), + colorArr[3] + ]; + }; + + var updateFillNode = function (el, style, zrEl) { + // TODO pattern + var fill = style.fill; + if (fill != null) { + // Modified from excanvas + if (fill instanceof Gradient) { + var gradientType; + var angle = 0; + var focus = [0, 0]; + // additional offset + var shift = 0; + // scale factor for offset + var expansion = 1; + var rect = zrEl.getBoundingRect(); + var rectWidth = rect.width; + var rectHeight = rect.height; + if (fill.type === 'linear') { + gradientType = 'gradient'; + var transform = zrEl.transform; + var p0 = [fill.x * rectWidth, fill.y * rectHeight]; + var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight]; + if (transform) { + applyTransform(p0, p0, transform); + applyTransform(p1, p1, transform); + } + var dx = p1[0] - p0[0]; + var dy = p1[1] - p0[1]; + angle = Math.atan2(dx, dy) * 180 / Math.PI; + // The angle should be a non-negative number. + if (angle < 0) { + angle += 360; + } + + // Very small angles produce an unexpected result because they are + // converted to a scientific notation string. + if (angle < 1e-6) { + angle = 0; + } + } + else { + gradientType = 'gradientradial'; + var p0 = [fill.x * rectWidth, fill.y * rectHeight]; + var transform = zrEl.transform; + var scale = zrEl.scale; + var width = rectWidth; + var height = rectHeight; + focus = [ + // Percent in bounding rect + (p0[0] - rect.x) / width, + (p0[1] - rect.y) / height + ]; + if (transform) { + applyTransform(p0, p0, transform); + } + + width /= scale[0] * Z; + height /= scale[1] * Z; + var dimension = mathMax(width, height); + shift = 2 * 0 / dimension; + expansion = 2 * fill.r / dimension - shift; + } + + // We need to sort the color stops in ascending order by offset, + // otherwise IE won't interpret it correctly. + var stops = fill.colorStops.slice(); + stops.sort(function(cs1, cs2) { + return cs1.offset - cs2.offset; + }); + + var length = stops.length; + // Color and alpha list of first and last stop + var colorAndAlphaList = []; + var colors = []; + for (var i = 0; i < length; i++) { + var stop = stops[i]; + var colorAndAlpha = getColorAndAlpha(stop.color); + colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]); + if (i === 0 || i === length - 1) { + colorAndAlphaList.push(colorAndAlpha); + } + } + + if (length >= 2) { + var color1 = colorAndAlphaList[0][0]; + var color2 = colorAndAlphaList[1][0]; + var opacity1 = colorAndAlphaList[0][1] * style.opacity; + var opacity2 = colorAndAlphaList[1][1] * style.opacity; + + el.type = gradientType; + el.method = 'none'; + el.focus = '100%'; + el.angle = angle; + el.color = color1; + el.color2 = color2; + el.colors = colors.join(','); + // When colors attribute is used, the meanings of opacity and o:opacity2 + // are reversed. + el.opacity = opacity2; + // FIXME g_o_:opacity ? + el.opacity2 = opacity1; + } + if (gradientType === 'radial') { + el.focusposition = focus.join(','); + } + } + else { + // FIXME Change from Gradient fill to color fill + setColorAndOpacity(el, fill, style.opacity); + } + } + }; + + var updateStrokeNode = function (el, style) { + // if (style.lineJoin != null) { + // el.joinstyle = style.lineJoin; + // } + // if (style.miterLimit != null) { + // el.miterlimit = style.miterLimit * Z; + // } + // if (style.lineCap != null) { + // el.endcap = style.lineCap; + // } + if (style.lineDash != null) { + el.dashstyle = style.lineDash.join(' '); + } + if (style.stroke != null && !(style.stroke instanceof Gradient)) { + setColorAndOpacity(el, style.stroke, style.opacity); + } + }; + + var updateFillAndStroke = function (vmlEl, type, style, zrEl) { + var isFill = type == 'fill'; + var el = vmlEl.getElementsByTagName(type)[0]; + // Stroke must have lineWidth + if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) { + vmlEl[isFill ? 'filled' : 'stroked'] = 'true'; + // FIXME Remove before updating, or set `colors` will throw error + if (style[type] instanceof Gradient) { + remove(vmlEl, el); + } + if (!el) { + el = vmlCore.createNode(type); + } + + isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style); + append(vmlEl, el); + } + else { + vmlEl[isFill ? 'filled' : 'stroked'] = 'false'; + remove(vmlEl, el); + } + }; + + var points = [[], [], []]; + var pathDataToString = function (data, m) { + var M = CMD.M; + var C = CMD.C; + var L = CMD.L; + var A = CMD.A; + var Q = CMD.Q; + + var str = []; + var nPoint; + var cmdStr; + var cmd; + var i; + var xi; + var yi; + for (i = 0; i < data.length;) { + cmd = data[i++]; + cmdStr = ''; + nPoint = 0; + switch (cmd) { + case M: + cmdStr = ' m '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points[0][0] = xi; + points[0][1] = yi; + break; + case L: + cmdStr = ' l '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points[0][0] = xi; + points[0][1] = yi; + break; + case Q: + case C: + cmdStr = ' c '; + nPoint = 3; + var x1 = data[i++]; + var y1 = data[i++]; + var x2 = data[i++]; + var y2 = data[i++]; + var x3; + var y3; + if (cmd === Q) { + // Convert quadratic to cubic using degree elevation + x3 = x2; + y3 = y2; + x2 = (x2 + 2 * x1) / 3; + y2 = (y2 + 2 * y1) / 3; + x1 = (xi + 2 * x1) / 3; + y1 = (yi + 2 * y1) / 3; + } + else { + x3 = data[i++]; + y3 = data[i++]; + } + points[0][0] = x1; + points[0][1] = y1; + points[1][0] = x2; + points[1][1] = y2; + points[2][0] = x3; + points[2][1] = y3; + + xi = x3; + yi = y3; + break; + case A: + var x = 0; + var y = 0; + var sx = 1; + var sy = 1; + var angle = 0; + if (m) { + // Extract SRT from matrix + x = m[4]; + y = m[5]; + sx = sqrt(m[0] * m[0] + m[1] * m[1]); + sy = sqrt(m[2] * m[2] + m[3] * m[3]); + angle = Math.atan2(-m[1] / sy, m[0] / sx); + } + + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++] + angle; + var endAngle = data[i++] + startAngle + angle; + // FIXME + // var psi = data[i++]; + i++; + var clockwise = data[i++]; + + var x0 = cx + cos(startAngle) * rx; + var y0 = cy + sin(startAngle) * ry; + + var x1 = cx + cos(endAngle) * rx; + var y1 = cy + sin(endAngle) * ry; + + var type = clockwise ? ' wa ' : ' at '; + if (Math.abs(x0 - x1) < 1e-10) { + // IE won't render arches drawn counter clockwise if x0 == x1. + if (Math.abs(endAngle - startAngle) > 1e-2) { + // Offset x0 by 1/80 of a pixel. Use something + // that can be represented in binary + if (clockwise) { + x0 += 270 / Z; + } + } + else { + // Avoid case draw full circle + if (Math.abs(y0 - cy) < 1e-10) { + if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) { + y1 -= 270 / Z; + } + else { + y1 += 270 / Z; + } + } + else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) { + x1 += 270 / Z; + } + else { + x1 -= 270 / Z; + } + } + } + str.push( + type, + round(((cx - rx) * sx + x) * Z - Z2), comma, + round(((cy - ry) * sy + y) * Z - Z2), comma, + round(((cx + rx) * sx + x) * Z - Z2), comma, + round(((cy + ry) * sy + y) * Z - Z2), comma, + round((x0 * sx + x) * Z - Z2), comma, + round((y0 * sy + y) * Z - Z2), comma, + round((x1 * sx + x) * Z - Z2), comma, + round((y1 * sy + y) * Z - Z2) + ); + + xi = x1; + yi = y1; + break; + case CMD.R: + var p0 = points[0]; + var p1 = points[1]; + // x0, y0 + p0[0] = data[i++]; + p0[1] = data[i++]; + // x1, y1 + p1[0] = p0[0] + data[i++]; + p1[1] = p0[1] + data[i++]; + + if (m) { + applyTransform(p0, p0, m); + applyTransform(p1, p1, m); + } + + p0[0] = round(p0[0] * Z - Z2); + p1[0] = round(p1[0] * Z - Z2); + p0[1] = round(p0[1] * Z - Z2); + p1[1] = round(p1[1] * Z - Z2); + str.push( + // x0, y0 + ' m ', p0[0], comma, p0[1], + // x1, y0 + ' l ', p1[0], comma, p0[1], + // x1, y1 + ' l ', p1[0], comma, p1[1], + // x0, y1 + ' l ', p0[0], comma, p1[1] + ); + break; + case CMD.Z: + // FIXME Update xi, yi + str.push(' x '); + } + + if (nPoint > 0) { + str.push(cmdStr); + for (var k = 0; k < nPoint; k++) { + var p = points[k]; + + m && applyTransform(p, p, m); + // 不 round 会非常慢 + str.push( + round(p[0] * Z - Z2), comma, round(p[1] * Z - Z2), + k < nPoint - 1 ? comma : '' + ); + } + } + } + + return str.join(''); + }; + + // Rewrite the original path method + Path.prototype.brushVML = function (vmlRoot) { + var style = this.style; + + var vmlEl = this._vmlEl; + if (!vmlEl) { + vmlEl = vmlCore.createNode('shape'); + initRootElStyle(vmlEl); + + this._vmlEl = vmlEl; + } + + updateFillAndStroke(vmlEl, 'fill', style, this); + updateFillAndStroke(vmlEl, 'stroke', style, this); + + var m = this.transform; + var needTransform = m != null; + var strokeEl = vmlEl.getElementsByTagName('stroke')[0]; + if (strokeEl) { + var lineWidth = style.lineWidth; + // Get the line scale. + // Determinant of this.m_ means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + if (needTransform && !style.strokeNoScale) { + var det = m[0] * m[3] - m[1] * m[2]; + lineWidth *= sqrt(abs(det)); + } + strokeEl.weight = lineWidth + 'px'; + } + + var path = this.path; + if (this.__dirtyPath) { + path.beginPath(); + this.buildPath(path, this.shape); + path.toStatic(); + this.__dirtyPath = false; + } + + vmlEl.path = pathDataToString(path.data, this.transform); + + vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Append to root + append(vmlRoot, vmlEl); + + // Text + if (style.text) { + this.drawRectText(vmlRoot, this.getBoundingRect()); + } + else { + this.removeRectText(vmlRoot); + } + }; + + Path.prototype.onRemove = function (vmlRoot) { + remove(vmlRoot, this._vmlEl); + this.removeRectText(vmlRoot); + }; + + Path.prototype.onAdd = function (vmlRoot) { + append(vmlRoot, this._vmlEl); + this.appendRectText(vmlRoot); + }; + + /*************************************************** + * IMAGE + **************************************************/ + var isImage = function (img) { + // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错 + return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG'; + // return img instanceof Image; + }; + + // Rewrite the original path method + ZImage.prototype.brushVML = function (vmlRoot) { + var style = this.style; + var image = style.image; + + // Image original width, height + var ow; + var oh; + + if (isImage(image)) { + var src = image.src; + if (src === this._imageSrc) { + ow = this._imageWidth; + oh = this._imageHeight; + } + else { + var imageRuntimeStyle = image.runtimeStyle; + var oldRuntimeWidth = imageRuntimeStyle.width; + var oldRuntimeHeight = imageRuntimeStyle.height; + imageRuntimeStyle.width = 'auto'; + imageRuntimeStyle.height = 'auto'; + + // get the original size + ow = image.width; + oh = image.height; + + // and remove overides + imageRuntimeStyle.width = oldRuntimeWidth; + imageRuntimeStyle.height = oldRuntimeHeight; + + // Caching image original width, height and src + this._imageSrc = src; + this._imageWidth = ow; + this._imageHeight = oh; + } + image = src; + } + else { + if (image === this._imageSrc) { + ow = this._imageWidth; + oh = this._imageHeight; + } + } + if (!image) { + return; + } + + var x = style.x || 0; + var y = style.y || 0; + + var dw = style.width; + var dh = style.height; + + var sw = style.sWidth; + var sh = style.sHeight; + var sx = style.sx || 0; + var sy = style.sy || 0; + + var hasCrop = sw && sh; + + var vmlEl = this._vmlEl; + if (!vmlEl) { + // FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。 + // vmlEl = vmlCore.createNode('group'); + vmlEl = vmlCore.doc.createElement('div'); + initRootElStyle(vmlEl); + + this._vmlEl = vmlEl; + } + + var vmlElStyle = vmlEl.style; + var hasRotation = false; + var m; + var scaleX = 1; + var scaleY = 1; + if (this.transform) { + m = this.transform; + scaleX = sqrt(m[0] * m[0] + m[1] * m[1]); + scaleY = sqrt(m[2] * m[2] + m[3] * m[3]); + + hasRotation = m[1] || m[2]; + } + if (hasRotation) { + // If filters are necessary (rotation exists), create them + // filters are bog-slow, so only create them if abbsolutely necessary + // The following check doesn't account for skews (which don't exist + // in the canvas spec (yet) anyway. + // From excanvas + var p0 = [x, y]; + var p1 = [x + dw, y]; + var p2 = [x, y + dh]; + var p3 = [x + dw, y + dh]; + applyTransform(p0, p0, m); + applyTransform(p1, p1, m); + applyTransform(p2, p2, m); + applyTransform(p3, p3, m); + + var maxX = mathMax(p0[0], p1[0], p2[0], p3[0]); + var maxY = mathMax(p0[1], p1[1], p2[1], p3[1]); + + var transformFilter = []; + transformFilter.push('M11=', m[0] / scaleX, comma, + 'M12=', m[2] / scaleY, comma, + 'M21=', m[1] / scaleX, comma, + 'M22=', m[3] / scaleY, comma, + 'Dx=', round(x * scaleX + m[4]), comma, + 'Dy=', round(y * scaleY + m[5])); + + vmlElStyle.padding = '0 ' + round(maxX) + 'px ' + round(maxY) + 'px 0'; + // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用 + vmlElStyle.filter = imageTransformPrefix + '.Matrix(' + + transformFilter.join('') + ', SizingMethod=clip)'; + + } + else { + if (m) { + x = x * scaleX + m[4]; + y = y * scaleY + m[5]; + } + vmlElStyle.filter = ''; + vmlElStyle.left = round(x) + 'px'; + vmlElStyle.top = round(y) + 'px'; + } + + var imageEl = this._imageEl; + var cropEl = this._cropEl; + + if (!imageEl) { + imageEl = vmlCore.doc.createElement('div'); + this._imageEl = imageEl; + } + var imageELStyle = imageEl.style; + if (hasCrop) { + // Needs know image original width and height + if (! (ow && oh)) { + var tmpImage = new Image(); + var self = this; + tmpImage.onload = function () { + tmpImage.onload = null; + ow = tmpImage.width; + oh = tmpImage.height; + // Adjust image width and height to fit the ratio destinationSize / sourceSize + imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; + + // Caching image original width, height and src + self._imageWidth = ow; + self._imageHeight = oh; + self._imageSrc = image; + }; + tmpImage.src = image; + } + else { + imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; + } + + if (! cropEl) { + cropEl = vmlCore.doc.createElement('div'); + cropEl.style.overflow = 'hidden'; + this._cropEl = cropEl; + } + var cropElStyle = cropEl.style; + cropElStyle.width = round((dw + sx * dw / sw) * scaleX); + cropElStyle.height = round((dh + sy * dh / sh) * scaleY); + cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx=' + + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')'; + + if (! cropEl.parentNode) { + vmlEl.appendChild(cropEl); + } + if (imageEl.parentNode != cropEl) { + cropEl.appendChild(imageEl); + } + } + else { + imageELStyle.width = round(scaleX * dw) + 'px'; + imageELStyle.height = round(scaleY * dh) + 'px'; + + vmlEl.appendChild(imageEl); + + if (cropEl && cropEl.parentNode) { + vmlEl.removeChild(cropEl); + this._cropEl = null; + } + } + + var filterStr = ''; + var alpha = style.opacity; + if (alpha < 1) { + filterStr += '.Alpha(opacity=' + round(alpha * 100) + ') '; + } + filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)'; + + imageELStyle.filter = filterStr; + + vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Append to root + append(vmlRoot, vmlEl); + + // Text + if (style.text) { + this.drawRectText(vmlRoot, this.getBoundingRect()); + } + }; + + ZImage.prototype.onRemove = function (vmlRoot) { + remove(vmlRoot, this._vmlEl); + + this._vmlEl = null; + this._cropEl = null; + this._imageEl = null; + + this.removeRectText(vmlRoot); + }; + + ZImage.prototype.onAdd = function (vmlRoot) { + append(vmlRoot, this._vmlEl); + this.appendRectText(vmlRoot); + }; + + + /*************************************************** + * TEXT + **************************************************/ + + var DEFAULT_STYLE_NORMAL = 'normal'; + + var fontStyleCache = {}; + var fontStyleCacheCount = 0; + var MAX_FONT_CACHE_SIZE = 100; + var fontEl = document.createElement('div'); + + var getFontStyle = function (fontString) { + var fontStyle = fontStyleCache[fontString]; + if (!fontStyle) { + // Clear cache + if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) { + fontStyleCacheCount = 0; + fontStyleCache = {}; + } + + var style = fontEl.style; + var fontFamily; + try { + style.font = fontString; + fontFamily = style.fontFamily.split(',')[0]; + } + catch (e) { + } + + fontStyle = { + style: style.fontStyle || DEFAULT_STYLE_NORMAL, + variant: style.fontVariant || DEFAULT_STYLE_NORMAL, + weight: style.fontWeight || DEFAULT_STYLE_NORMAL, + size: parseFloat(style.fontSize || 12) | 0, + family: fontFamily || 'Microsoft YaHei' + }; + + fontStyleCache[fontString] = fontStyle; + fontStyleCacheCount++; + } + return fontStyle; + }; + + var textMeasureEl; + // Overwrite measure text method + textContain.measureText = function (text, textFont) { + var doc = vmlCore.doc; + if (!textMeasureEl) { + textMeasureEl = doc.createElement('div'); + textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' + + 'padding:0;margin:0;border:none;white-space:pre;'; + vmlCore.doc.body.appendChild(textMeasureEl); + } + + try { + textMeasureEl.style.font = textFont; + } catch (ex) { + // Ignore failures to set to invalid font. + } + textMeasureEl.innerHTML = ''; + // Don't use innerHTML or innerText because they allow markup/whitespace. + textMeasureEl.appendChild(doc.createTextNode(text)); + return { + width: textMeasureEl.offsetWidth + }; + }; + + var tmpRect = new BoundingRect(); + + var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) { + + var style = this.style; + var text = style.text; + if (!text) { + return; + } + + var x; + var y; + var align = style.textAlign; + var fontStyle = getFontStyle(style.textFont); + // FIXME encodeHtmlAttribute ? + var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' + + fontStyle.size + 'px "' + fontStyle.family + '"'; + + var baseline = style.textBaseline; + var verticalAlign = style.textVerticalAlign; + + textRect = textRect || textContain.getBoundingRect(text, font, align, baseline); + + // Transform rect to view space + var m = this.transform; + // Ignore transform for text in other element + if (m && !fromTextEl) { + tmpRect.copy(rect); + tmpRect.applyTransform(m); + rect = tmpRect; + } + + if (!fromTextEl) { + var textPosition = style.textPosition; + var distance = style.textDistance; + // Text position represented by coord + if (textPosition instanceof Array) { + x = rect.x + parsePercent(textPosition[0], rect.width); + y = rect.y + parsePercent(textPosition[1], rect.height); + + align = align || 'left'; + baseline = baseline || 'top'; + } + else { + var res = textContain.adjustTextPositionOnRect( + textPosition, rect, textRect, distance + ); + x = res.x; + y = res.y; + + // Default align and baseline when has textPosition + align = align || res.textAlign; + baseline = baseline || res.textBaseline; + } + } + else { + x = rect.x; + y = rect.y; + } + if (verticalAlign) { + switch (verticalAlign) { + case 'middle': + y -= textRect.height / 2; + break; + case 'bottom': + y -= textRect.height; + break; + // 'top' + } + // Ignore baseline + baseline = 'top'; + } + + var fontSize = fontStyle.size; + // 1.75 is an arbitrary number, as there is no info about the text baseline + switch (baseline) { + case 'hanging': + case 'top': + y += fontSize / 1.75; + break; + case 'middle': + break; + default: + // case null: + // case 'alphabetic': + // case 'ideographic': + // case 'bottom': + y -= fontSize / 2.25; + break; + } + switch (align) { + case 'left': + break; + case 'center': + x -= textRect.width / 2; + break; + case 'right': + x -= textRect.width; + break; + // case 'end': + // align = elementStyle.direction == 'ltr' ? 'right' : 'left'; + // break; + // case 'start': + // align = elementStyle.direction == 'rtl' ? 'right' : 'left'; + // break; + // default: + // align = 'left'; + } + + var createNode = vmlCore.createNode; + + var textVmlEl = this._textVmlEl; + var pathEl; + var textPathEl; + var skewEl; + if (!textVmlEl) { + textVmlEl = createNode('line'); + pathEl = createNode('path'); + textPathEl = createNode('textpath'); + skewEl = createNode('skew'); + + // FIXME Why here is not cammel case + // Align 'center' seems wrong + textPathEl.style['v-text-align'] = 'left'; + + initRootElStyle(textVmlEl); + + pathEl.textpathok = true; + textPathEl.on = true; + + textVmlEl.from = '0 0'; + textVmlEl.to = '1000 0.05'; + + append(textVmlEl, skewEl); + append(textVmlEl, pathEl); + append(textVmlEl, textPathEl); + + this._textVmlEl = textVmlEl; + } + else { + // 这里是在前面 appendChild 保证顺序的前提下 + skewEl = textVmlEl.firstChild; + pathEl = skewEl.nextSibling; + textPathEl = pathEl.nextSibling; + } + + var coords = [x, y]; + var textVmlElStyle = textVmlEl.style; + // Ignore transform for text in other element + if (m && fromTextEl) { + applyTransform(coords, coords, m); + + skewEl.on = true; + + skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma + + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0'; + + // Text position + skewEl.offset = (round(coords[0]) || 0) + ',' + (round(coords[1]) || 0); + // Left top point as origin + skewEl.origin = '0 0'; + + textVmlElStyle.left = '0px'; + textVmlElStyle.top = '0px'; + } + else { + skewEl.on = false; + textVmlElStyle.left = round(x) + 'px'; + textVmlElStyle.top = round(y) + 'px'; + } + + textPathEl.string = encodeHtmlAttribute(text); + // TODO + try { + textPathEl.style.font = font; + } + // Error font format + catch (e) {} + + updateFillAndStroke(textVmlEl, 'fill', { + fill: fromTextEl ? style.fill : style.textFill, + opacity: style.opacity + }, this); + updateFillAndStroke(textVmlEl, 'stroke', { + stroke: fromTextEl ? style.stroke : style.textStroke, + opacity: style.opacity, + lineDash: style.lineDash + }, this); + + textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Attached to root + append(vmlRoot, textVmlEl); + }; + + var removeRectText = function (vmlRoot) { + remove(vmlRoot, this._textVmlEl); + this._textVmlEl = null; + }; + + var appendRectText = function (vmlRoot) { + append(vmlRoot, this._textVmlEl); + }; + + var list = [RectText, Displayable, ZImage, Path, Text]; + + // In case Displayable has been mixed in RectText + for (var i = 0; i < list.length; i++) { + var proto = list[i].prototype; + proto.drawRectText = drawRectText; + proto.removeRectText = removeRectText; + proto.appendRectText = appendRectText; + } + + Text.prototype.brushVML = function (vmlRoot) { + var style = this.style; + if (style.text) { + this.drawRectText(vmlRoot, { + x: style.x || 0, y: style.y || 0, + width: 0, height: 0 + }, this.getBoundingRect(), true); + } + else { + this.removeRectText(vmlRoot); + } + }; + + Text.prototype.onRemove = function (vmlRoot) { + this.removeRectText(vmlRoot); + }; + + Text.prototype.onAdd = function (vmlRoot) { + this.appendRectText(vmlRoot); + }; + } + + +/***/ }, +/* 381 */ +/***/ function(module, exports, __webpack_require__) { + + + + if (!__webpack_require__(2).canvasSupported) { + var urn = 'urn:schemas-microsoft-com:vml'; + + var createNode; + var win = window; + var doc = win.document; + + var vmlInited = false; + + try { + !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn); + createNode = function (tagName) { + return doc.createElement(''); + }; + } + catch (e) { + createNode = function (tagName) { + return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">'); + }; + } + + // From raphael + var initVML = function () { + if (vmlInited) { + return; + } + vmlInited = true; + + var styleSheets = doc.styleSheets; + if (styleSheets.length < 31) { + doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)'); + } + else { + // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx + styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)'); + } + }; + + // Not useing return to avoid error when converting to CommonJS module + module.exports = { + doc: doc, + initVML: initVML, + createNode: createNode + }; + } + + +/***/ }, +/* 382 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * VML Painter. + * + * @module zrender/vml/Painter + */ + + + + var zrLog = __webpack_require__(40); + var vmlCore = __webpack_require__(381); + + function parseInt10(val) { + return parseInt(val, 10); + } + + /** + * @alias module:zrender/vml/Painter + */ + function VMLPainter(root, storage) { + + vmlCore.initVML(); + + this.root = root; + + this.storage = storage; + + var vmlViewport = document.createElement('div'); + + var vmlRoot = document.createElement('div'); + + vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;'; + + vmlRoot.style.cssText = 'position:absolute;left:0;top:0;'; + + root.appendChild(vmlViewport); + + this._vmlRoot = vmlRoot; + this._vmlViewport = vmlViewport; + + this.resize(); + + // Modify storage + var oldDelFromMap = storage.delFromMap; + var oldAddToMap = storage.addToMap; + storage.delFromMap = function (elId) { + var el = storage.get(elId); + + oldDelFromMap.call(storage, elId); + + if (el) { + el.onRemove && el.onRemove(vmlRoot); + } + }; + + storage.addToMap = function (el) { + // Displayable already has a vml node + el.onAdd && el.onAdd(vmlRoot); + + oldAddToMap.call(storage, el); + }; + + this._firstPaint = true; + } + + VMLPainter.prototype = { + + constructor: VMLPainter, + + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._vmlViewport; + }, + + /** + * 刷新 + */ + refresh: function () { + + var list = this.storage.getDisplayList(true, true); + + this._paintList(list); + }, + + _paintList: function (list) { + var vmlRoot = this._vmlRoot; + for (var i = 0; i < list.length; i++) { + var el = list[i]; + if (el.invisible || el.ignore) { + if (!el.__alreadyNotVisible) { + el.onRemove(vmlRoot); + } + // Set as already invisible + el.__alreadyNotVisible = true; + } + else { + if (el.__alreadyNotVisible) { + el.onAdd(vmlRoot); + } + el.__alreadyNotVisible = false; + if (el.__dirty) { + el.beforeBrush && el.beforeBrush(); + (el.brushVML || el.brush).call(el, vmlRoot); + el.afterBrush && el.afterBrush(); + } + } + el.__dirty = false; + } + + if (this._firstPaint) { + // Detached from document at first time + // to avoid page refreshing too many times + + // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变 + this._vmlViewport.appendChild(vmlRoot); + this._firstPaint = false; + } + }, + + resize: function () { + var width = this._getWidth(); + var height = this._getHeight(); + + if (this._width != width && this._height != height) { + this._width = width; + this._height = height; + + var vmlViewportStyle = this._vmlViewport.style; + vmlViewportStyle.width = width + 'px'; + vmlViewportStyle.height = height + 'px'; + } + }, + + dispose: function () { + this.root.innerHTML = ''; + + this._vmlRoot = + this._vmlViewport = + this.storage = null; + }, + + getWidth: function () { + return this._width; + }, + + getHeight: function () { + return this._height; + }, + + clear: function () { + this.root.removeChild(this.vmlViewport); + }, + + _getWidth: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientWidth || parseInt10(stl.width)) + - parseInt10(stl.paddingLeft) + - parseInt10(stl.paddingRight)) | 0; + }, + + _getHeight: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientHeight || parseInt10(stl.height)) + - parseInt10(stl.paddingTop) + - parseInt10(stl.paddingBottom)) | 0; + } + }; + + // Not supported methods + function createMethodNotSupport(method) { + return function () { + zrLog('In IE8.0 VML mode painter not support method "' + method + '"'); + }; + } + + var notSupportedMethods = [ + 'getLayer', 'insertLayer', 'eachLayer', 'eachBuildinLayer', 'eachOtherLayer', 'getLayers', + 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage' + ]; + + for (var i = 0; i < notSupportedMethods.length; i++) { + var name = notSupportedMethods[i]; + VMLPainter.prototype[name] = createMethodNotSupport(name); + } + + module.exports = VMLPainter; + + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/views/macarons.js b/src/main/webapp/WEB-INF/views/macarons.js new file mode 100644 index 0000000..398aa3f --- /dev/null +++ b/src/main/webapp/WEB-INF/views/macarons.js @@ -0,0 +1,198 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports', 'echarts'], factory); + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + factory(exports, require('echarts')); + } else { + // Browser globals + factory({}, root.echarts); + } +}(this, function (exports, echarts) { + var log = function (msg) { + if (typeof console !== 'undefined') { + console && console.error && console.error(msg); + } + }; + if (!echarts) { + log('ECharts is not Loaded'); + return; + } + + var colorPalette = [ + '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80', + '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa', + '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050', + '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089' + ]; + + + var theme = { + color: colorPalette, + + title: { + textStyle: { + fontWeight: 'normal', + color: '#008acd' + } + }, + + visualMap: { + itemWidth: 15, + color: ['#5ab1ef','#e0ffff'] + }, + + toolbox: { + iconStyle: { + normal: { + borderColor: colorPalette[0] + } + } + }, + + tooltip: { + backgroundColor: 'rgba(50,50,50,0.5)', + axisPointer : { + type : 'line', + lineStyle : { + color: '#008acd' + }, + crossStyle: { + color: '#008acd' + }, + shadowStyle : { + color: 'rgba(200,200,200,0.2)' + } + } + }, + + dataZoom: { + dataBackgroundColor: '#efefff', + fillerColor: 'rgba(182,162,222,0.2)', + handleColor: '#008acd' + }, + + grid: { + borderColor: '#eee' + }, + + categoryAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + valueAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitArea : { + show : true, + areaStyle : { + color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)'] + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + timeline : { + lineStyle : { + color : '#008acd' + }, + controlStyle : { + normal : { color : '#008acd'}, + emphasis : { color : '#008acd'} + }, + symbol : 'emptyCircle', + symbolSize : 3 + }, + + line: { + smooth : true, + symbol: 'emptyCircle', + symbolSize: 3 + }, + + candlestick: { + itemStyle: { + normal: { + color: '#d87a80', + color0: '#2ec7c9', + lineStyle: { + color: '#d87a80', + color0: '#2ec7c9' + } + } + } + }, + + scatter: { + symbol: 'circle', + symbolSize: 4 + }, + + map: { + label: { + normal: { + textStyle: { + color: '#d87a80' + } + } + }, + itemStyle: { + normal: { + borderColor: '#eee', + areaColor: '#ddd' + }, + emphasis: { + areaColor: '#fe994e' + } + } + }, + + graph: { + color: colorPalette + }, + + gauge : { + axisLine: { + lineStyle: { + color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']], + width: 10 + } + }, + axisTick: { + splitNumber: 10, + length :15, + lineStyle: { + color: 'auto' + } + }, + splitLine: { + length :22, + lineStyle: { + color: 'auto' + } + }, + pointer : { + width : 5 + } + } + }; + + echarts.registerTheme('macarons', theme); +})); \ No newline at end of file diff --git a/src/main/webapp/WEB-INF/views/performanceList.jsp b/src/main/webapp/WEB-INF/views/performanceList.jsp new file mode 100644 index 0000000..dc362bb --- /dev/null +++ b/src/main/webapp/WEB-INF/views/performanceList.jsp @@ -0,0 +1,377 @@ +<%@ page contentType="text/html;charset=UTF-8" %> +<%@ include file="/WEB-INF/views/include/taglib.jsp"%> + + + 生产实绩管理 + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
全选计划周期顺序号计划周期名称计划周期编码产品名称产品编码任务数量生产数量合格品数量一次合格品数量废品数量计量单位最后编辑时间是否按时完成相关任务单
${performance.planningcycle.jhzqmc}${performance.planningcycle.jhzqbm}${performance.material.wlmc}${performance.material.wlbm}${performance.rwsl}${performance.scsl}${performance.hgpsl}${performance.ychgpsl}${performance.fpsl}${performance.jldw}${fns:getDictLabel(performance.sfwc, 'yes_no', ' ')}查询
+   + +   + + + + + + + + + + + +
+ + + diff --git a/src/main/webapp/WEB-INF/views/result.jsp b/src/main/webapp/WEB-INF/views/result.jsp new file mode 100644 index 0000000..32e79ba --- /dev/null +++ b/src/main/webapp/WEB-INF/views/result.jsp @@ -0,0 +1,130 @@ + +<%-- + Created by IntelliJ IDEA. + User: inst1 + Date: 2017/8/6 + Time: 10:28 + To change this template use File | Settings | File Templates. +--%> +<%@ page contentType="text/html;charset=UTF-8" language="java" %> + + + Title + + + + +
+ + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000..9420526 --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,66 @@ + + + SjspWeb + + index + + + contextConfigLocation + classpath:spring.xml + + + org.springframework.web.context.ContextLoaderListener + + + encodingFilter + org.springframework.web.filter.CharacterEncodingFilter + + encoding + UTF-8 + + + forceEncoding + true + + + + encodingFilter + /* + + + spring-dispatcher + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + classpath:spring-mvc.xml + + 1 + + + + spring-dispatcher + / + + + default + *.css + + + + default + *.gif + + + + + default + *.jpg + + + + default + *.js + + + + \ No newline at end of file diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp new file mode 100644 index 0000000..c38169b --- /dev/null +++ b/src/main/webapp/index.jsp @@ -0,0 +1,5 @@ + + +

Hello World!

+ + diff --git a/src/main/webapp/static/echarts.js b/src/main/webapp/static/echarts.js new file mode 100644 index 0000000..60c5d61 --- /dev/null +++ b/src/main/webapp/static/echarts.js @@ -0,0 +1,63738 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["echarts"] = factory(); + else + root["echarts"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; + +/******/ // The require function +/******/ function __webpack_require__(moduleId) { + +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) +/******/ return installedModules[moduleId].exports; + +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ exports: {}, +/******/ id: moduleId, +/******/ loaded: false +/******/ }; + +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + +/******/ // Flag the module as loaded +/******/ module.loaded = true; + +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } + + +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; + +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; + +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; + +/******/ // Load entry module and return exports +/******/ return __webpack_require__(0); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Export echarts as CommonJS module + */ + module.exports = __webpack_require__(1); + + // Import all charts and components + __webpack_require__(99); + __webpack_require__(133); + __webpack_require__(138); + __webpack_require__(147); + __webpack_require__(151); + + __webpack_require__(161); + __webpack_require__(182); + __webpack_require__(194); + __webpack_require__(215); + __webpack_require__(219); + __webpack_require__(223); + __webpack_require__(238); + __webpack_require__(244); + __webpack_require__(251); + __webpack_require__(257); + __webpack_require__(261); + __webpack_require__(269); + + __webpack_require__(112); + __webpack_require__(273); + __webpack_require__(279); + __webpack_require__(283); + __webpack_require__(294); + __webpack_require__(224); + __webpack_require__(297); + __webpack_require__(303); + + __webpack_require__(315); + + __webpack_require__(316); + __webpack_require__(330); + + __webpack_require__(345); + __webpack_require__(351); + __webpack_require__(354); + + __webpack_require__(357); + __webpack_require__(366); + + __webpack_require__(379); + + +/***/ }, +/* 1 */ +/***/ function(module, exports, __webpack_require__) { + + // Enable DEV mode when using source code without build. which has no __DEV__ variable + // In build process 'typeof __DEV__' will be replace with 'boolean' + // So this code will be removed or disabled anyway after built. + if (false) { + // In browser + if (typeof window !== 'undefined') { + window.__DEV__ = true; + } + // In node + else if (typeof global !== 'undefined') { + global.__DEV__ = true; + } + } + + /*! + * ECharts, a javascript interactive chart library. + * + * Copyright (c) 2015, Baidu Inc. + * All rights reserved. + * + * LICENSE + * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt + */ + + /** + * @module echarts + */ + + + var env = __webpack_require__(2); + + var GlobalModel = __webpack_require__(3); + var ExtensionAPI = __webpack_require__(25); + var CoordinateSystemManager = __webpack_require__(26); + var OptionManager = __webpack_require__(27); + + var ComponentModel = __webpack_require__(19); + var SeriesModel = __webpack_require__(28); + + var ComponentView = __webpack_require__(29); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + + var zrender = __webpack_require__(81); + var zrUtil = __webpack_require__(4); + var colorTool = __webpack_require__(39); + var Eventful = __webpack_require__(33); + var timsort = __webpack_require__(85); + + var each = zrUtil.each; + + var PRIORITY_PROCESSOR_FILTER = 1000; + var PRIORITY_PROCESSOR_STATISTIC = 5000; + + + var PRIORITY_VISUAL_LAYOUT = 1000; + var PRIORITY_VISUAL_GLOBAL = 2000; + var PRIORITY_VISUAL_CHART = 3000; + var PRIORITY_VISUAL_COMPONENT = 4000; + var PRIORITY_VISUAL_BRUSH = 5000; + + // Main process have three entries: `setOption`, `dispatchAction` and `resize`, + // where they must not be invoked nestedly, except the only case: invoke + // dispatchAction with updateMethod "none" in main process. + // This flag is used to carry out this rule. + // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). + var IN_MAIN_PROCESS = '__flag_in_main_process'; + var HAS_GRADIENT_OR_PATTERN_BG = '_hasGradientOrPatternBg'; + + + var OPTION_UPDATED = '_optionUpdated'; + + function createRegisterEventWithLowercaseName(method) { + return function (eventName, handler, context) { + // Event name is all lowercase + eventName = eventName && eventName.toLowerCase(); + Eventful.prototype[method].call(this, eventName, handler, context); + }; + } + /** + * @module echarts~MessageCenter + */ + function MessageCenter() { + Eventful.call(this); + } + MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on'); + MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off'); + MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one'); + zrUtil.mixin(MessageCenter, Eventful); + /** + * @module echarts~ECharts + */ + function ECharts (dom, theme, opts) { + opts = opts || {}; + + // Get theme by name + if (typeof theme === 'string') { + theme = themeStorage[theme]; + } + + /** + * @type {string} + */ + this.id; + /** + * Group id + * @type {string} + */ + this.group; + /** + * @type {HTMLDomElement} + * @private + */ + this._dom = dom; + /** + * @type {module:zrender/ZRender} + * @private + */ + this._zr = zrender.init(dom, { + renderer: opts.renderer || 'canvas', + devicePixelRatio: opts.devicePixelRatio + }); + + /** + * @type {Object} + * @private + */ + this._theme = zrUtil.clone(theme); + + /** + * @type {Array.} + * @private + */ + this._chartsViews = []; + + /** + * @type {Object.} + * @private + */ + this._chartsMap = {}; + + /** + * @type {Array.} + * @private + */ + this._componentsViews = []; + + /** + * @type {Object.} + * @private + */ + this._componentsMap = {}; + + /** + * @type {module:echarts/ExtensionAPI} + * @private + */ + this._api = new ExtensionAPI(this); + + /** + * @type {module:echarts/CoordinateSystem} + * @private + */ + this._coordSysMgr = new CoordinateSystemManager(); + + Eventful.call(this); + + /** + * @type {module:echarts~MessageCenter} + * @private + */ + this._messageCenter = new MessageCenter(); + + // Init mouse events + this._initEvents(); + + // In case some people write `window.onresize = chart.resize` + this.resize = zrUtil.bind(this.resize, this); + + // Can't dispatch action during rendering procedure + this._pendingActions = []; + // Sort on demand + function prioritySortFunc(a, b) { + return a.prio - b.prio; + } + timsort(visualFuncs, prioritySortFunc); + timsort(dataProcessorFuncs, prioritySortFunc); + + this._zr.animation.on('frame', this._onframe, this); + } + + var echartsProto = ECharts.prototype; + + echartsProto._onframe = function () { + // Lazy update + if (this[OPTION_UPDATED]) { + + this[IN_MAIN_PROCESS] = true; + + updateMethods.prepareAndUpdate.call(this); + + this[IN_MAIN_PROCESS] = false; + + this[OPTION_UPDATED] = false; + } + }; + /** + * @return {HTMLDomElement} + */ + echartsProto.getDom = function () { + return this._dom; + }; + + /** + * @return {module:zrender~ZRender} + */ + echartsProto.getZr = function () { + return this._zr; + }; + + /** + * @param {Object} option + * @param {boolean} notMerge + * @param {boolean} [lazyUpdate=false] Useful when setOption frequently. + */ + echartsProto.setOption = function (option, notMerge, lazyUpdate) { + if (true) { + zrUtil.assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.'); + } + + this[IN_MAIN_PROCESS] = true; + + if (!this._model || notMerge) { + var optionManager = new OptionManager(this._api); + var theme = this._theme; + var ecModel = this._model = new GlobalModel(null, null, theme, optionManager); + ecModel.init(null, null, theme, optionManager); + } + + this._model.setOption(option, optionPreprocessorFuncs); + + if (lazyUpdate) { + this[OPTION_UPDATED] = true; + } + else { + updateMethods.prepareAndUpdate.call(this); + this._zr.refreshImmediately(); + this[OPTION_UPDATED] = false; + } + + this[IN_MAIN_PROCESS] = false; + + this._flushPendingActions(); + }; + + /** + * @DEPRECATED + */ + echartsProto.setTheme = function () { + console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); + }; + + /** + * @return {module:echarts/model/Global} + */ + echartsProto.getModel = function () { + return this._model; + }; + + /** + * @return {Object} + */ + echartsProto.getOption = function () { + return this._model && this._model.getOption(); + }; + + /** + * @return {number} + */ + echartsProto.getWidth = function () { + return this._zr.getWidth(); + }; + + /** + * @return {number} + */ + echartsProto.getHeight = function () { + return this._zr.getHeight(); + }; + + /** + * Get canvas which has all thing rendered + * @param {Object} opts + * @param {string} [opts.backgroundColor] + */ + echartsProto.getRenderedCanvas = function (opts) { + if (!env.canvasSupported) { + return; + } + opts = opts || {}; + opts.pixelRatio = opts.pixelRatio || 1; + opts.backgroundColor = opts.backgroundColor + || this._model.get('backgroundColor'); + var zr = this._zr; + var list = zr.storage.getDisplayList(); + // Stop animations + zrUtil.each(list, function (el) { + el.stopAnimation(true); + }); + return zr.painter.getRenderedCanvas(opts); + }; + /** + * @return {string} + * @param {Object} opts + * @param {string} [opts.type='png'] + * @param {string} [opts.pixelRatio=1] + * @param {string} [opts.backgroundColor] + */ + echartsProto.getDataURL = function (opts) { + opts = opts || {}; + var excludeComponents = opts.excludeComponents; + var ecModel = this._model; + var excludesComponentViews = []; + var self = this; + + each(excludeComponents, function (componentType) { + ecModel.eachComponent({ + mainType: componentType + }, function (component) { + var view = self._componentsMap[component.__viewId]; + if (!view.group.ignore) { + excludesComponentViews.push(view); + view.group.ignore = true; + } + }); + }); + + var url = this.getRenderedCanvas(opts).toDataURL( + 'image/' + (opts && opts.type || 'png') + ); + + each(excludesComponentViews, function (view) { + view.group.ignore = false; + }); + return url; + }; + + + /** + * @return {string} + * @param {Object} opts + * @param {string} [opts.type='png'] + * @param {string} [opts.pixelRatio=1] + * @param {string} [opts.backgroundColor] + */ + echartsProto.getConnectedDataURL = function (opts) { + if (!env.canvasSupported) { + return; + } + var groupId = this.group; + var mathMin = Math.min; + var mathMax = Math.max; + var MAX_NUMBER = Infinity; + if (connectedGroups[groupId]) { + var left = MAX_NUMBER; + var top = MAX_NUMBER; + var right = -MAX_NUMBER; + var bottom = -MAX_NUMBER; + var canvasList = []; + var dpr = (opts && opts.pixelRatio) || 1; + for (var id in instances) { + var chart = instances[id]; + if (chart.group === groupId) { + var canvas = chart.getRenderedCanvas( + zrUtil.clone(opts) + ); + var boundingRect = chart.getDom().getBoundingClientRect(); + left = mathMin(boundingRect.left, left); + top = mathMin(boundingRect.top, top); + right = mathMax(boundingRect.right, right); + bottom = mathMax(boundingRect.bottom, bottom); + canvasList.push({ + dom: canvas, + left: boundingRect.left, + top: boundingRect.top + }); + } + } + + left *= dpr; + top *= dpr; + right *= dpr; + bottom *= dpr; + var width = right - left; + var height = bottom - top; + var targetCanvas = zrUtil.createCanvas(); + targetCanvas.width = width; + targetCanvas.height = height; + var zr = zrender.init(targetCanvas); + + each(canvasList, function (item) { + var img = new graphic.Image({ + style: { + x: item.left * dpr - left, + y: item.top * dpr - top, + image: item.dom + } + }); + zr.add(img); + }); + zr.refreshImmediately(); + + return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); + } + else { + return this.getDataURL(opts); + } + }; + + var updateMethods = { + + + /** + * @param {Object} payload + * @private + */ + update: function (payload) { + // console.time && console.time('update'); + + var ecModel = this._model; + var api = this._api; + var coordSysMgr = this._coordSysMgr; + var zr = this._zr; + // update before setOption + if (!ecModel) { + return; + } + + // Fixme First time update ? + ecModel.restoreData(); + + // TODO + // Save total ecModel here for undo/redo (after restoring data and before processing data). + // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. + + // Create new coordinate system each update + // In LineView may save the old coordinate system and use it to get the orignal point + coordSysMgr.create(this._model, this._api); + + processData.call(this, ecModel, api); + + stackSeriesData.call(this, ecModel); + + coordSysMgr.update(ecModel, api); + + doVisualEncoding.call(this, ecModel, payload); + + doRender.call(this, ecModel, payload); + + // Set background + var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; + + var painter = zr.painter; + // TODO all use clearColor ? + if (painter.isSingleCanvas && painter.isSingleCanvas()) { + zr.configLayer(0, { + clearColor: backgroundColor + }); + } + else { + // In IE8 + if (!env.canvasSupported) { + var colorArr = colorTool.parse(backgroundColor); + backgroundColor = colorTool.stringify(colorArr, 'rgb'); + if (colorArr[3] === 0) { + backgroundColor = 'transparent'; + } + } + if (backgroundColor.colorStops || backgroundColor.image) { + // Gradient background + // FIXME Fixed layer? + zr.configLayer(0, { + clearColor: backgroundColor + }); + this[HAS_GRADIENT_OR_PATTERN_BG] = true; + + this._dom.style.background = 'transparent'; + } + else { + if (this[HAS_GRADIENT_OR_PATTERN_BG]) { + zr.configLayer(0, { + clearColor: null + }); + } + this[HAS_GRADIENT_OR_PATTERN_BG] = false; + + this._dom.style.background = backgroundColor; + } + } + + // console.time && console.timeEnd('update'); + }, + + // PENDING + /** + * @param {Object} payload + * @private + */ + updateView: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + ecModel.eachSeries(function (seriesModel) { + seriesModel.getData().clearAllVisual(); + }); + + doVisualEncoding.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateView', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + updateVisual: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + ecModel.eachSeries(function (seriesModel) { + seriesModel.getData().clearAllVisual(); + }); + + doVisualEncoding.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + updateLayout: function (payload) { + var ecModel = this._model; + + // update before setOption + if (!ecModel) { + return; + } + + doLayout.call(this, ecModel, payload); + + invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload); + }, + + /** + * @param {Object} payload + * @private + */ + highlight: function (payload) { + toggleHighlight.call(this, 'highlight', payload); + }, + + /** + * @param {Object} payload + * @private + */ + downplay: function (payload) { + toggleHighlight.call(this, 'downplay', payload); + }, + + /** + * @param {Object} payload + * @private + */ + prepareAndUpdate: function (payload) { + var ecModel = this._model; + + prepareView.call(this, 'component', ecModel); + + prepareView.call(this, 'chart', ecModel); + + updateMethods.update.call(this, payload); + } + }; + + /** + * @param {Object} payload + * @private + */ + function toggleHighlight(method, payload) { + var ecModel = this._model; + + // dispatchAction before setOption + if (!ecModel) { + return; + } + + ecModel.eachComponent( + {mainType: 'series', query: payload}, + function (seriesModel, index) { + var chartView = this._chartsMap[seriesModel.__viewId]; + if (chartView && chartView.__alive) { + chartView[method]( + seriesModel, ecModel, this._api, payload + ); + } + }, + this + ); + } + + /** + * Resize the chart + */ + echartsProto.resize = function () { + if (true) { + zrUtil.assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.'); + } + + this[IN_MAIN_PROCESS] = true; + + this._zr.resize(); + + var optionChanged = this._model && this._model.resetOption('media'); + updateMethods[optionChanged ? 'prepareAndUpdate' : 'update'].call(this); + + // Resize loading effect + this._loadingFX && this._loadingFX.resize(); + + this[IN_MAIN_PROCESS] = false; + + this._flushPendingActions(); + }; + + /** + * Show loading effect + * @param {string} [name='default'] + * @param {Object} [cfg] + */ + echartsProto.showLoading = function (name, cfg) { + if (zrUtil.isObject(name)) { + cfg = name; + name = ''; + } + name = name || 'default'; + + this.hideLoading(); + if (!loadingEffects[name]) { + if (true) { + console.warn('Loading effects ' + name + ' not exists.'); + } + return; + } + var el = loadingEffects[name](this._api, cfg); + var zr = this._zr; + this._loadingFX = el; + + zr.add(el); + }; + + /** + * Hide loading effect + */ + echartsProto.hideLoading = function () { + this._loadingFX && this._zr.remove(this._loadingFX); + this._loadingFX = null; + }; + + /** + * @param {Object} eventObj + * @return {Object} + */ + echartsProto.makeActionFromEvent = function (eventObj) { + var payload = zrUtil.extend({}, eventObj); + payload.type = eventActionMap[eventObj.type]; + return payload; + }; + + /** + * @pubilc + * @param {Object} payload + * @param {string} [payload.type] Action type + * @param {boolean} [silent=false] Whether trigger event. + */ + echartsProto.dispatchAction = function (payload, silent) { + var actionWrap = actions[payload.type]; + if (!actionWrap) { + return; + } + + var actionInfo = actionWrap.actionInfo; + var updateMethod = actionInfo.update || 'update'; + + // if (__DEV__) { + // zrUtil.assert( + // !this[IN_MAIN_PROCESS], + // '`dispatchAction` should not be called during main process.' + // + 'unless updateMathod is "none".' + // ); + // } + + // May dispatchAction in rendering procedure + if (this[IN_MAIN_PROCESS]) { + this._pendingActions.push(payload); + return; + } + + this[IN_MAIN_PROCESS] = true; + + var payloads = [payload]; + var batched = false; + // Batch action + if (payload.batch) { + batched = true; + payloads = zrUtil.map(payload.batch, function (item) { + item = zrUtil.defaults(zrUtil.extend({}, item), payload); + item.batch = null; + return item; + }); + } + + var eventObjBatch = []; + var eventObj; + var isHighlightOrDownplay = payload.type === 'highlight' || payload.type === 'downplay'; + for (var i = 0; i < payloads.length; i++) { + var batchItem = payloads[i]; + // Action can specify the event by return it. + eventObj = actionWrap.action(batchItem, this._model); + // Emit event outside + eventObj = eventObj || zrUtil.extend({}, batchItem); + // Convert type to eventType + eventObj.type = actionInfo.event || eventObj.type; + eventObjBatch.push(eventObj); + + // Highlight and downplay are special. + isHighlightOrDownplay && updateMethods[updateMethod].call(this, batchItem); + } + + if (updateMethod !== 'none' && !isHighlightOrDownplay) { + // Still dirty + if (this[OPTION_UPDATED]) { + // FIXME Pass payload ? + updateMethods.prepareAndUpdate.call(this, payload); + this[OPTION_UPDATED] = false; + } + else { + updateMethods[updateMethod].call(this, payload); + } + } + + // Follow the rule of action batch + if (batched) { + eventObj = { + type: actionInfo.event || payload.type, + batch: eventObjBatch + }; + } + else { + eventObj = eventObjBatch[0]; + } + + this[IN_MAIN_PROCESS] = false; + + !silent && this._messageCenter.trigger(eventObj.type, eventObj); + + this._flushPendingActions(); + + }; + + echartsProto._flushPendingActions = function () { + var pendingActions = this._pendingActions; + while (pendingActions.length) { + var payload = pendingActions.shift(); + this.dispatchAction(payload); + } + }; + + /** + * Register event + * @method + */ + echartsProto.on = createRegisterEventWithLowercaseName('on'); + echartsProto.off = createRegisterEventWithLowercaseName('off'); + echartsProto.one = createRegisterEventWithLowercaseName('one'); + + /** + * @param {string} methodName + * @private + */ + function invokeUpdateMethod(methodName, ecModel, payload) { + var api = this._api; + + // Update all components + each(this._componentsViews, function (component) { + var componentModel = component.__model; + component[methodName](componentModel, ecModel, api, payload); + + updateZ(componentModel, component); + }, this); + + // Upate all charts + ecModel.eachSeries(function (seriesModel, idx) { + var chart = this._chartsMap[seriesModel.__viewId]; + chart[methodName](seriesModel, ecModel, api, payload); + + updateZ(seriesModel, chart); + + updateProgressiveAndBlend(seriesModel, chart); + }, this); + + // If use hover layer + updateHoverLayerStatus(this._zr, ecModel); + } + + /** + * Prepare view instances of charts and components + * @param {module:echarts/model/Global} ecModel + * @private + */ + function prepareView(type, ecModel) { + var isComponent = type === 'component'; + var viewList = isComponent ? this._componentsViews : this._chartsViews; + var viewMap = isComponent ? this._componentsMap : this._chartsMap; + var zr = this._zr; + + for (var i = 0; i < viewList.length; i++) { + viewList[i].__alive = false; + } + + ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) { + if (isComponent) { + if (componentType === 'series') { + return; + } + } + else { + model = componentType; + } + + // Consider: id same and type changed. + var viewId = model.id + '_' + model.type; + var view = viewMap[viewId]; + if (!view) { + var classType = ComponentModel.parseClassType(model.type); + var Clazz = isComponent + ? ComponentView.getClass(classType.main, classType.sub) + : ChartView.getClass(classType.sub); + if (Clazz) { + view = new Clazz(); + view.init(ecModel, this._api); + viewMap[viewId] = view; + viewList.push(view); + zr.add(view.group); + } + else { + // Error + return; + } + } + + model.__viewId = viewId; + view.__alive = true; + view.__id = viewId; + view.__model = model; + }, this); + + for (var i = 0; i < viewList.length;) { + var view = viewList[i]; + if (!view.__alive) { + zr.remove(view.group); + view.dispose(ecModel, this._api); + viewList.splice(i, 1); + delete viewMap[view.__id]; + } + else { + i++; + } + } + } + + /** + * Processor data in each series + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function processData(ecModel, api) { + each(dataProcessorFuncs, function (process) { + process.func(ecModel, api); + }); + } + + /** + * @private + */ + function stackSeriesData(ecModel) { + var stackedDataMap = {}; + ecModel.eachSeries(function (series) { + var stack = series.get('stack'); + var data = series.getData(); + if (stack && data.type === 'list') { + var previousStack = stackedDataMap[stack]; + if (previousStack) { + data.stackedOn = previousStack; + } + stackedDataMap[stack] = data; + } + }); + } + + /** + * Layout before each chart render there series, special visual encoding stage + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function doLayout(ecModel, payload) { + var api = this._api; + each(visualFuncs, function (visual) { + if (visual.isLayout) { + visual.func(ecModel, api, payload); + } + }); + } + + /** + * Encode visual infomation from data after data processing + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + function doVisualEncoding(ecModel, payload) { + var api = this._api; + ecModel.clearColorPalette(); + ecModel.eachSeries(function (seriesModel) { + seriesModel.clearColorPalette(); + }); + each(visualFuncs, function (visual) { + visual.func(ecModel, api, payload); + }); + } + + /** + * Render each chart and component + * @private + */ + function doRender(ecModel, payload) { + var api = this._api; + // Render all components + each(this._componentsViews, function (componentView) { + var componentModel = componentView.__model; + componentView.render(componentModel, ecModel, api, payload); + + updateZ(componentModel, componentView); + }, this); + + each(this._chartsViews, function (chart) { + chart.__alive = false; + }, this); + + // Render all charts + ecModel.eachSeries(function (seriesModel, idx) { + var chartView = this._chartsMap[seriesModel.__viewId]; + chartView.__alive = true; + chartView.render(seriesModel, ecModel, api, payload); + + chartView.group.silent = !!seriesModel.get('silent'); + + updateZ(seriesModel, chartView); + + updateProgressiveAndBlend(seriesModel, chartView); + + }, this); + + // If use hover layer + updateHoverLayerStatus(this._zr, ecModel); + + // Remove groups of unrendered charts + each(this._chartsViews, function (chart) { + if (!chart.__alive) { + chart.remove(ecModel, api); + } + }, this); + } + + var MOUSE_EVENT_NAMES = [ + 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout' + ]; + /** + * @private + */ + echartsProto._initEvents = function () { + each(MOUSE_EVENT_NAMES, function (eveName) { + this._zr.on(eveName, function (e) { + var ecModel = this.getModel(); + var el = e.target; + if (el && el.dataIndex != null) { + var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); + var params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {}; + params.event = e; + params.type = eveName; + this.trigger(eveName, params); + } + // If element has custom eventData of components + else if (el && el.eventData) { + this.trigger(eveName, el.eventData); + } + }, this); + }, this); + + each(eventActionMap, function (actionType, eventType) { + this._messageCenter.on(eventType, function (event) { + this.trigger(eventType, event); + }, this); + }, this); + }; + + /** + * @return {boolean} + */ + echartsProto.isDisposed = function () { + return this._disposed; + }; + + /** + * Clear + */ + echartsProto.clear = function () { + this.setOption({ series: [] }, true); + }; + /** + * Dispose instance + */ + echartsProto.dispose = function () { + if (this._disposed) { + if (true) { + console.warn('Instance ' + this.id + ' has been disposed'); + } + return; + } + this._disposed = true; + + var api = this._api; + var ecModel = this._model; + + each(this._componentsViews, function (component) { + component.dispose(ecModel, api); + }); + each(this._chartsViews, function (chart) { + chart.dispose(ecModel, api); + }); + + // Dispose after all views disposed + this._zr.dispose(); + + delete instances[this.id]; + }; + + zrUtil.mixin(ECharts, Eventful); + + function updateHoverLayerStatus(zr, ecModel) { + var storage = zr.storage; + var elCount = 0; + storage.traverse(function (el) { + if (!el.isGroup) { + elCount++; + } + }); + if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) { + storage.traverse(function (el) { + if (!el.isGroup) { + el.useHoverLayer = true; + } + }); + } + } + /** + * Update chart progressive and blend. + * @param {module:echarts/model/Series|module:echarts/model/Component} model + * @param {module:echarts/view/Component|module:echarts/view/Chart} view + */ + function updateProgressiveAndBlend(seriesModel, chartView) { + // Progressive configuration + var elCount = 0; + chartView.group.traverse(function (el) { + if (el.type !== 'group' && !el.ignore) { + elCount++; + } + }); + var frameDrawNum = +seriesModel.get('progressive'); + var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node; + if (needProgressive) { + chartView.group.traverse(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + el.progressive = needProgressive ? + Math.floor(elCount++ / frameDrawNum) : -1; + if (needProgressive) { + el.stopAnimation(true); + } + } + }); + } + + // Blend configration + var blendMode = seriesModel.get('blendMode') || null; + if (true) { + if (!env.canvasSupported && blendMode && blendMode !== 'source-over') { + console.warn('Only canvas support blendMode'); + } + } + chartView.group.traverse(function (el) { + // FIXME marker and other components + if (!el.isGroup) { + el.setStyle('blend', blendMode); + } + }); + } + /** + * @param {module:echarts/model/Series|module:echarts/model/Component} model + * @param {module:echarts/view/Component|module:echarts/view/Chart} view + */ + function updateZ(model, view) { + var z = model.get('z'); + var zlevel = model.get('zlevel'); + // Set z and zlevel + view.group.traverse(function (el) { + if (el.type !== 'group') { + z != null && (el.z = z); + zlevel != null && (el.zlevel = zlevel); + } + }); + } + /** + * @type {Array.} + * @inner + */ + var actions = []; + + /** + * Map eventType to actionType + * @type {Object} + */ + var eventActionMap = {}; + + /** + * Data processor functions of each stage + * @type {Array.>} + * @inner + */ + var dataProcessorFuncs = []; + + /** + * @type {Array.} + * @inner + */ + var optionPreprocessorFuncs = []; + + /** + * Visual encoding functions of each stage + * @type {Array.>} + * @inner + */ + var visualFuncs = []; + /** + * Theme storage + * @type {Object.} + */ + var themeStorage = {}; + /** + * Loading effects + */ + var loadingEffects = {}; + + + var instances = {}; + var connectedGroups = {}; + + var idBase = new Date() - 0; + var groupIdBase = new Date() - 0; + var DOM_ATTRIBUTE_KEY = '_echarts_instance_'; + /** + * @alias module:echarts + */ + var echarts = { + /** + * @type {number} + */ + version: '3.2.3', + dependencies: { + zrender: '3.1.3' + } + }; + + function enableConnect(chart) { + + var STATUS_PENDING = 0; + var STATUS_UPDATING = 1; + var STATUS_UPDATED = 2; + var STATUS_KEY = '__connectUpdateStatus'; + function updateConnectedChartsStatus(charts, status) { + for (var i = 0; i < charts.length; i++) { + var otherChart = charts[i]; + otherChart[STATUS_KEY] = status; + } + } + zrUtil.each(eventActionMap, function (actionType, eventType) { + chart._messageCenter.on(eventType, function (event) { + if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) { + var action = chart.makeActionFromEvent(event); + var otherCharts = []; + for (var id in instances) { + var otherChart = instances[id]; + if (otherChart !== chart && otherChart.group === chart.group) { + otherCharts.push(otherChart); + } + } + updateConnectedChartsStatus(otherCharts, STATUS_PENDING); + each(otherCharts, function (otherChart) { + if (otherChart[STATUS_KEY] !== STATUS_UPDATING) { + otherChart.dispatchAction(action); + } + }); + updateConnectedChartsStatus(otherCharts, STATUS_UPDATED); + } + }); + }); + + } + /** + * @param {HTMLDomElement} dom + * @param {Object} [theme] + * @param {Object} opts + */ + echarts.init = function (dom, theme, opts) { + if (true) { + // Check version + if ((zrender.version.replace('.', '') - 0) < (echarts.dependencies.zrender.replace('.', '') - 0)) { + throw new Error( + 'ZRender ' + zrender.version + + ' is too old for ECharts ' + echarts.version + + '. Current version need ZRender ' + + echarts.dependencies.zrender + '+' + ); + } + if (!dom) { + throw new Error('Initialize failed: invalid dom.'); + } + if (zrUtil.isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth || !dom.clientHeight)) { + console.warn('Can\'t get dom width or height'); + } + } + + var chart = new ECharts(dom, theme, opts); + chart.id = 'ec_' + idBase++; + instances[chart.id] = chart; + + dom.setAttribute && + dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id); + + enableConnect(chart); + + return chart; + }; + + /** + * @return {string|Array.} groupId + */ + echarts.connect = function (groupId) { + // Is array of charts + if (zrUtil.isArray(groupId)) { + var charts = groupId; + groupId = null; + // If any chart has group + zrUtil.each(charts, function (chart) { + if (chart.group != null) { + groupId = chart.group; + } + }); + groupId = groupId || ('g_' + groupIdBase++); + zrUtil.each(charts, function (chart) { + chart.group = groupId; + }); + } + connectedGroups[groupId] = true; + return groupId; + }; + + /** + * @return {string} groupId + */ + echarts.disConnect = function (groupId) { + connectedGroups[groupId] = false; + }; + + /** + * Dispose a chart instance + * @param {module:echarts~ECharts|HTMLDomElement|string} chart + */ + echarts.dispose = function (chart) { + if (zrUtil.isDom(chart)) { + chart = echarts.getInstanceByDom(chart); + } + else if (typeof chart === 'string') { + chart = instances[chart]; + } + if ((chart instanceof ECharts) && !chart.isDisposed()) { + chart.dispose(); + } + }; + + /** + * @param {HTMLDomElement} dom + * @return {echarts~ECharts} + */ + echarts.getInstanceByDom = function (dom) { + var key = dom.getAttribute(DOM_ATTRIBUTE_KEY); + return instances[key]; + }; + /** + * @param {string} key + * @return {echarts~ECharts} + */ + echarts.getInstanceById = function (key) { + return instances[key]; + }; + + /** + * Register theme + */ + echarts.registerTheme = function (name, theme) { + themeStorage[name] = theme; + }; + + /** + * Register option preprocessor + * @param {Function} preprocessorFunc + */ + echarts.registerPreprocessor = function (preprocessorFunc) { + optionPreprocessorFuncs.push(preprocessorFunc); + }; + + /** + * @param {number} [priority=1000] + * @param {Function} processorFunc + */ + echarts.registerProcessor = function (priority, processorFunc) { + if (typeof priority === 'function') { + processorFunc = priority; + priority = PRIORITY_PROCESSOR_FILTER; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown processor priority'); + } + } + dataProcessorFuncs.push({ + prio: priority, + func: processorFunc + }); + }; + + /** + * Usage: + * registerAction('someAction', 'someEvent', function () { ... }); + * registerAction('someAction', function () { ... }); + * registerAction( + * {type: 'someAction', event: 'someEvent', update: 'updateView'}, + * function () { ... } + * ); + * + * @param {(string|Object)} actionInfo + * @param {string} actionInfo.type + * @param {string} [actionInfo.event] + * @param {string} [actionInfo.update] + * @param {string} [eventName] + * @param {Function} action + */ + echarts.registerAction = function (actionInfo, eventName, action) { + if (typeof eventName === 'function') { + action = eventName; + eventName = ''; + } + var actionType = zrUtil.isObject(actionInfo) + ? actionInfo.type + : ([actionInfo, actionInfo = { + event: eventName + }][0]); + + // Event name is all lowercase + actionInfo.event = (actionInfo.event || actionType).toLowerCase(); + eventName = actionInfo.event; + + if (!actions[actionType]) { + actions[actionType] = {action: action, actionInfo: actionInfo}; + } + eventActionMap[eventName] = actionType; + }; + + /** + * @param {string} type + * @param {*} CoordinateSystem + */ + echarts.registerCoordinateSystem = function (type, CoordinateSystem) { + CoordinateSystemManager.register(type, CoordinateSystem); + }; + + /** + * Layout is a special stage of visual encoding + * Most visual encoding like color are common for different chart + * But each chart has it's own layout algorithm + * + * @param {number} [priority=1000] + * @param {Function} layoutFunc + */ + echarts.registerLayout = function (priority, layoutFunc) { + if (typeof priority === 'function') { + layoutFunc = priority; + priority = PRIORITY_VISUAL_LAYOUT; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown layout priority'); + } + } + visualFuncs.push({ + prio: priority, + func: layoutFunc, + isLayout: true + }); + }; + + /** + * @param {number} [priority=3000] + * @param {Function} visualFunc + */ + echarts.registerVisual = function (priority, visualFunc) { + if (typeof priority === 'function') { + visualFunc = priority; + priority = PRIORITY_VISUAL_CHART; + } + if (true) { + if (isNaN(priority)) { + throw new Error('Unkown visual priority'); + } + } + visualFuncs.push({ + prio: priority, + func: visualFunc + }); + }; + + /** + * @param {string} name + */ + echarts.registerLoading = function (name, loadingFx) { + loadingEffects[name] = loadingFx; + }; + + + var parseClassType = ComponentModel.parseClassType; + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendComponentModel = function (opts, superClass) { + var Clazz = ComponentModel; + if (superClass) { + var classType = parseClassType(superClass); + Clazz = ComponentModel.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendComponentView = function (opts, superClass) { + var Clazz = ComponentView; + if (superClass) { + var classType = parseClassType(superClass); + Clazz = ComponentView.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendSeriesModel = function (opts, superClass) { + var Clazz = SeriesModel; + if (superClass) { + superClass = 'series.' + superClass.replace('series.', ''); + var classType = parseClassType(superClass); + Clazz = SeriesModel.getClass(classType.main, classType.sub, true); + } + return Clazz.extend(opts); + }; + + /** + * @param {Object} opts + * @param {string} [superClass] + */ + echarts.extendChartView = function (opts, superClass) { + var Clazz = ChartView; + if (superClass) { + superClass.replace('series.', ''); + var classType = parseClassType(superClass); + Clazz = ChartView.getClass(classType.main, true); + } + return Clazz.extend(opts); + }; + + /** + * ZRender need a canvas context to do measureText. + * But in node environment canvas may be created by node-canvas. + * So we need to specify how to create a canvas instead of using document.createElement('canvas') + * + * Be careful of using it in the browser. + * + * @param {Function} creator + * @example + * var Canvas = require('canvas'); + * var echarts = require('echarts'); + * echarts.setCanvasCreator(function () { + * // Small size is enough. + * return new Canvas(32, 32); + * }); + */ + echarts.setCanvasCreator = function (creator) { + zrUtil.createCanvas = creator; + }; + + echarts.registerVisual(PRIORITY_VISUAL_GLOBAL, __webpack_require__(93)); + echarts.registerPreprocessor(__webpack_require__(94)); + echarts.registerLoading('default', __webpack_require__(96)); + + // Default action + echarts.registerAction({ + type: 'highlight', + event: 'highlight', + update: 'highlight' + }, zrUtil.noop); + echarts.registerAction({ + type: 'downplay', + event: 'downplay', + update: 'downplay' + }, zrUtil.noop); + + + // -------- + // Exports + // -------- + // + echarts.List = __webpack_require__(97); + echarts.Model = __webpack_require__(12); + + echarts.graphic = __webpack_require__(43); + echarts.number = __webpack_require__(7); + echarts.format = __webpack_require__(6); + echarts.matrix = __webpack_require__(11); + echarts.vector = __webpack_require__(10); + echarts.color = __webpack_require__(39); + + echarts.util = {}; + each([ + 'map', 'each', 'filter', 'indexOf', 'inherits', + 'reduce', 'filter', 'bind', 'curry', 'isArray', + 'isString', 'isObject', 'isFunction', 'extend', 'defaults' + ], + function (name) { + echarts.util[name] = zrUtil[name]; + } + ); + + // PRIORITY + echarts.PRIORITY = { + PROCESSOR: { + FILTER: PRIORITY_PROCESSOR_FILTER, + STATISTIC: PRIORITY_PROCESSOR_STATISTIC + }, + VISUAL: { + LAYOUT: PRIORITY_VISUAL_LAYOUT, + GLOBAL: PRIORITY_VISUAL_GLOBAL, + CHART: PRIORITY_VISUAL_CHART, + COMPONENT: PRIORITY_VISUAL_COMPONENT, + BRUSH: PRIORITY_VISUAL_BRUSH + } + }; + + module.exports = echarts; + + +/***/ }, +/* 2 */ +/***/ function(module, exports) { + + /** + * echarts设备环境识别 + * + * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。 + * @author firede[firede@firede.us] + * @desc thanks zepto. + */ + + var env = {}; + if (typeof navigator === 'undefined') { + // In node + env = { + browser: {}, + os: {}, + node: true, + // Assume canvas is supported + canvasSupported: true + }; + } + else { + env = detect(navigator.userAgent); + } + + module.exports = env; + + // Zepto.js + // (c) 2010-2013 Thomas Fuchs + // Zepto.js may be freely distributed under the MIT license. + + function detect(ua) { + var os = {}; + var browser = {}; + // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/); + // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); + // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); + // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); + // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); + // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/); + // var touchpad = webos && ua.match(/TouchPad/); + // var kindle = ua.match(/Kindle\/([\d.]+)/); + // var silk = ua.match(/Silk\/([\d._]+)/); + // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/); + // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/); + // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/); + // var playbook = ua.match(/PlayBook/); + // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/); + var firefox = ua.match(/Firefox\/([\d.]+)/); + // var safari = webkit && ua.match(/Mobile\//) && !chrome; + // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome; + var ie = ua.match(/MSIE\s([\d.]+)/) + // IE 11 Trident/7.0; rv:11.0 + || ua.match(/Trident\/.+?rv:(([\d.]+))/); + var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+ + + // Todo: clean this up with a better OS/browser seperation: + // - discern (more) between multiple browsers on android + // - decide if kindle fire in silk mode is android or not + // - Firefox on Android doesn't specify the Android version + // - possibly devide in os, device and browser hashes + + // if (browser.webkit = !!webkit) browser.version = webkit[1]; + + // if (android) os.android = true, os.version = android[2]; + // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.'); + // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.'); + // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null; + // if (webos) os.webos = true, os.version = webos[2]; + // if (touchpad) os.touchpad = true; + // if (blackberry) os.blackberry = true, os.version = blackberry[2]; + // if (bb10) os.bb10 = true, os.version = bb10[2]; + // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2]; + // if (playbook) browser.playbook = true; + // if (kindle) os.kindle = true, os.version = kindle[1]; + // if (silk) browser.silk = true, browser.version = silk[1]; + // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true; + // if (chrome) browser.chrome = true, browser.version = chrome[1]; + if (firefox) browser.firefox = true, browser.version = firefox[1]; + // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true; + // if (webview) browser.webview = true; + if (ie) { + browser.ie = true; browser.version = ie[1]; + } + if (ie) { + browser.ie = true; + browser.version = ie[1]; + } + if (edge) { + browser.edge = true; + browser.version = edge[1]; + } + + // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || + // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/))); + // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos || + // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || + // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/)))); + + return { + browser: browser, + os: os, + node: false, + // 原生canvas支持,改极端点了 + // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9) + canvasSupported : document.createElement('canvas').getContext ? true : false, + // @see + // works on most browsers + // IE10/11 does not support touch event, and MS Edge supports them but not by + // default, so we dont check navigator.maxTouchPoints for them here. + touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge, + // . + pointerEventsSupported: 'onpointerdown' in window + // Firefox supports pointer but not by default, + // only MS browsers are reliable on pointer events currently. + && (browser.edge || (browser.ie && browser.version >= 10)) + }; + } + + +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * ECharts global model + * + * @module {echarts/model/Global} + * + */ + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var Model = __webpack_require__(12); + var each = zrUtil.each; + var filter = zrUtil.filter; + var map = zrUtil.map; + var isArray = zrUtil.isArray; + var indexOf = zrUtil.indexOf; + var isObject = zrUtil.isObject; + + var ComponentModel = __webpack_require__(19); + + var globalDefault = __webpack_require__(23); + + var OPTION_INNER_KEY = '\0_ec_inner'; + + /** + * @alias module:echarts/model/Global + * + * @param {Object} option + * @param {module:echarts/model/Model} parentModel + * @param {Object} theme + */ + var GlobalModel = Model.extend({ + + constructor: GlobalModel, + + init: function (option, parentModel, theme, optionManager) { + theme = theme || {}; + + this.option = null; // Mark as not initialized. + + /** + * @type {module:echarts/model/Model} + * @private + */ + this._theme = new Model(theme); + + /** + * @type {module:echarts/model/OptionManager} + */ + this._optionManager = optionManager; + }, + + setOption: function (option, optionPreprocessorFuncs) { + zrUtil.assert( + !(OPTION_INNER_KEY in option), + 'please use chart.getOption()' + ); + + this._optionManager.setOption(option, optionPreprocessorFuncs); + + this.resetOption(); + }, + + /** + * @param {string} type null/undefined: reset all. + * 'recreate': force recreate all. + * 'timeline': only reset timeline option + * 'media': only reset media query option + * @return {boolean} Whether option changed. + */ + resetOption: function (type) { + var optionChanged = false; + var optionManager = this._optionManager; + + if (!type || type === 'recreate') { + var baseOption = optionManager.mountOption(type === 'recreate'); + + if (!this.option || type === 'recreate') { + initBase.call(this, baseOption); + } + else { + this.restoreData(); + this.mergeOption(baseOption); + } + optionChanged = true; + } + + if (type === 'timeline' || type === 'media') { + this.restoreData(); + } + + if (!type || type === 'recreate' || type === 'timeline') { + var timelineOption = optionManager.getTimelineOption(this); + timelineOption && (this.mergeOption(timelineOption), optionChanged = true); + } + + if (!type || type === 'recreate' || type === 'media') { + var mediaOptions = optionManager.getMediaOption(this, this._api); + if (mediaOptions.length) { + each(mediaOptions, function (mediaOption) { + this.mergeOption(mediaOption, optionChanged = true); + }, this); + } + } + + return optionChanged; + }, + + /** + * @protected + */ + mergeOption: function (newOption) { + var option = this.option; + var componentsMap = this._componentsMap; + var newCptTypes = []; + + // 如果不存在对应的 component model 则直接 merge + each(newOption, function (componentOption, mainType) { + if (componentOption == null) { + return; + } + + if (!ComponentModel.hasClass(mainType)) { + option[mainType] = option[mainType] == null + ? zrUtil.clone(componentOption) + : zrUtil.merge(option[mainType], componentOption, true); + } + else { + newCptTypes.push(mainType); + } + }); + + // FIXME OPTION 同步是否要改回原来的 + ComponentModel.topologicalTravel( + newCptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this + ); + + this._seriesIndices = this._seriesIndices || []; + + function visitComponent(mainType, dependencies) { + var newCptOptionList = modelUtil.normalizeToArray(newOption[mainType]); + + var mapResult = modelUtil.mappingToExists( + componentsMap[mainType], newCptOptionList + ); + + makeKeyInfo(mainType, mapResult); + + var dependentModels = getComponentsByTypes( + componentsMap, dependencies + ); + + option[mainType] = []; + componentsMap[mainType] = []; + + each(mapResult, function (resultItem, index) { + var componentModel = resultItem.exist; + var newCptOption = resultItem.option; + + zrUtil.assert( + isObject(newCptOption) || componentModel, + 'Empty component definition' + ); + + // Consider where is no new option and should be merged using {}, + // see removeEdgeAndAdd in topologicalTravel and + // ComponentModel.getAllClassMainTypes. + if (!newCptOption) { + componentModel.mergeOption({}, this); + componentModel.optionUpdated({}, false); + } + else { + var ComponentModelClass = ComponentModel.getClass( + mainType, resultItem.keyInfo.subType, true + ); + + if (componentModel && componentModel instanceof ComponentModelClass) { + componentModel.mergeOption(newCptOption, this); + componentModel.optionUpdated(newCptOption, false); + } + else { + // PENDING Global as parent ? + var extraOpt = zrUtil.extend( + { + dependentModels: dependentModels, + componentIndex: index + }, + resultItem.keyInfo + ); + componentModel = new ComponentModelClass( + newCptOption, this, this, extraOpt + ); + componentModel.init(newCptOption, this, this, extraOpt); + // Call optionUpdated after init. + // newCptOption has been used as componentModel.option + // and may be merged with theme and default, so pass null + // to avoid confusion. + componentModel.optionUpdated(null, true); + } + } + + componentsMap[mainType][index] = componentModel; + option[mainType][index] = componentModel.option; + }, this); + + // Backup series for filtering. + if (mainType === 'series') { + this._seriesIndices = createSeriesIndices(componentsMap.series); + } + } + }, + + /** + * Get option for output (cloned option and inner info removed) + * @public + * @return {Object} + */ + getOption: function () { + var option = zrUtil.clone(this.option); + + each(option, function (opts, mainType) { + if (ComponentModel.hasClass(mainType)) { + var opts = modelUtil.normalizeToArray(opts); + for (var i = opts.length - 1; i >= 0; i--) { + // Remove options with inner id. + if (modelUtil.isIdInner(opts[i])) { + opts.splice(i, 1); + } + } + option[mainType] = opts; + } + }); + + delete option[OPTION_INNER_KEY]; + + return option; + }, + + /** + * @return {module:echarts/model/Model} + */ + getTheme: function () { + return this._theme; + }, + + /** + * @param {string} mainType + * @param {number} [idx=0] + * @return {module:echarts/model/Component} + */ + getComponent: function (mainType, idx) { + var list = this._componentsMap[mainType]; + if (list) { + return list[idx || 0]; + } + }, + + /** + * @param {Object} condition + * @param {string} condition.mainType + * @param {string} [condition.subType] If ignore, only query by mainType + * @param {number} [condition.index] Either input index or id or name. + * @param {string} [condition.id] Either input index or id or name. + * @param {string} [condition.name] Either input index or id or name. + * @return {Array.} + */ + queryComponents: function (condition) { + var mainType = condition.mainType; + if (!mainType) { + return []; + } + + var index = condition.index; + var id = condition.id; + var name = condition.name; + + var cpts = this._componentsMap[mainType]; + + if (!cpts || !cpts.length) { + return []; + } + + var result; + + if (index != null) { + if (!isArray(index)) { + index = [index]; + } + result = filter(map(index, function (idx) { + return cpts[idx]; + }), function (val) { + return !!val; + }); + } + else if (id != null) { + var isIdArray = isArray(id); + result = filter(cpts, function (cpt) { + return (isIdArray && indexOf(id, cpt.id) >= 0) + || (!isIdArray && cpt.id === id); + }); + } + else if (name != null) { + var isNameArray = isArray(name); + result = filter(cpts, function (cpt) { + return (isNameArray && indexOf(name, cpt.name) >= 0) + || (!isNameArray && cpt.name === name); + }); + } + else { + // Return all components with mainType + result = cpts; + } + + return filterBySubType(result, condition); + }, + + /** + * The interface is different from queryComponents, + * which is convenient for inner usage. + * + * @usage + * var result = findComponents( + * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} + * ); + * var result = findComponents( + * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} + * ); + * var result = findComponents( + * {mainType: 'series'}, + * function (model, index) {...} + * ); + * // result like [component0, componnet1, ...] + * + * @param {Object} condition + * @param {string} condition.mainType Mandatory. + * @param {string} [condition.subType] Optional. + * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName}, + * where xxx is mainType. + * If query attribute is null/undefined or has no index/id/name, + * do not filtering by query conditions, which is convenient for + * no-payload situations or when target of action is global. + * @param {Function} [condition.filter] parameter: component, return boolean. + * @return {Array.} + */ + findComponents: function (condition) { + var query = condition.query; + var mainType = condition.mainType; + + var queryCond = getQueryCond(query); + var result = queryCond + ? this.queryComponents(queryCond) + : this._componentsMap[mainType]; + + return doFilter(filterBySubType(result, condition)); + + function getQueryCond(q) { + var indexAttr = mainType + 'Index'; + var idAttr = mainType + 'Id'; + var nameAttr = mainType + 'Name'; + return q && ( + q.hasOwnProperty(indexAttr) + || q.hasOwnProperty(idAttr) + || q.hasOwnProperty(nameAttr) + ) + ? { + mainType: mainType, + // subType will be filtered finally. + index: q[indexAttr], + id: q[idAttr], + name: q[nameAttr] + } + : null; + } + + function doFilter(res) { + return condition.filter + ? filter(res, condition.filter) + : res; + } + }, + + /** + * @usage + * eachComponent('legend', function (legendModel, index) { + * ... + * }); + * eachComponent(function (componentType, model, index) { + * // componentType does not include subType + * // (componentType is 'xxx' but not 'xxx.aa') + * }); + * eachComponent( + * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}, + * function (model, index) {...} + * ); + * eachComponent( + * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}, + * function (model, index) {...} + * ); + * + * @param {string|Object=} mainType When mainType is object, the definition + * is the same as the method 'findComponents'. + * @param {Function} cb + * @param {*} context + */ + eachComponent: function (mainType, cb, context) { + var componentsMap = this._componentsMap; + + if (typeof mainType === 'function') { + context = cb; + cb = mainType; + each(componentsMap, function (components, componentType) { + each(components, function (component, index) { + cb.call(context, componentType, component, index); + }); + }); + } + else if (zrUtil.isString(mainType)) { + each(componentsMap[mainType], cb, context); + } + else if (isObject(mainType)) { + var queryResult = this.findComponents(mainType); + each(queryResult, cb, context); + } + }, + + /** + * @param {string} name + * @return {Array.} + */ + getSeriesByName: function (name) { + var series = this._componentsMap.series; + return filter(series, function (oneSeries) { + return oneSeries.name === name; + }); + }, + + /** + * @param {number} seriesIndex + * @return {module:echarts/model/Series} + */ + getSeriesByIndex: function (seriesIndex) { + return this._componentsMap.series[seriesIndex]; + }, + + /** + * @param {string} subType + * @return {Array.} + */ + getSeriesByType: function (subType) { + var series = this._componentsMap.series; + return filter(series, function (oneSeries) { + return oneSeries.subType === subType; + }); + }, + + /** + * @return {Array.} + */ + getSeries: function () { + return this._componentsMap.series.slice(); + }, + + /** + * After filtering, series may be different + * frome raw series. + * + * @param {Function} cb + * @param {*} context + */ + eachSeries: function (cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.series[rawSeriesIndex]; + cb.call(context, series, rawSeriesIndex); + }, this); + }, + + /** + * Iterate raw series before filtered. + * + * @param {Function} cb + * @param {*} context + */ + eachRawSeries: function (cb, context) { + each(this._componentsMap.series, cb, context); + }, + + /** + * After filtering, series may be different. + * frome raw series. + * + * @parma {string} subType + * @param {Function} cb + * @param {*} context + */ + eachSeriesByType: function (subType, cb, context) { + assertSeriesInitialized(this); + each(this._seriesIndices, function (rawSeriesIndex) { + var series = this._componentsMap.series[rawSeriesIndex]; + if (series.subType === subType) { + cb.call(context, series, rawSeriesIndex); + } + }, this); + }, + + /** + * Iterate raw series before filtered of given type. + * + * @parma {string} subType + * @param {Function} cb + * @param {*} context + */ + eachRawSeriesByType: function (subType, cb, context) { + return each(this.getSeriesByType(subType), cb, context); + }, + + /** + * @param {module:echarts/model/Series} seriesModel + */ + isSeriesFiltered: function (seriesModel) { + assertSeriesInitialized(this); + return zrUtil.indexOf(this._seriesIndices, seriesModel.componentIndex) < 0; + }, + + /** + * @param {Function} cb + * @param {*} context + */ + filterSeries: function (cb, context) { + assertSeriesInitialized(this); + var filteredSeries = filter( + this._componentsMap.series, cb, context + ); + this._seriesIndices = createSeriesIndices(filteredSeries); + }, + + restoreData: function () { + var componentsMap = this._componentsMap; + + this._seriesIndices = createSeriesIndices(componentsMap.series); + + var componentTypes = []; + each(componentsMap, function (components, componentType) { + componentTypes.push(componentType); + }); + + ComponentModel.topologicalTravel( + componentTypes, + ComponentModel.getAllClassMainTypes(), + function (componentType, dependencies) { + each(componentsMap[componentType], function (component) { + component.restoreData(); + }); + } + ); + } + + }); + + /** + * @inner + */ + function mergeTheme(option, theme) { + for (var name in theme) { + // 如果有 component model 则把具体的 merge 逻辑交给该 model 处理 + if (!ComponentModel.hasClass(name)) { + if (typeof theme[name] === 'object') { + option[name] = !option[name] + ? zrUtil.clone(theme[name]) + : zrUtil.merge(option[name], theme[name], false); + } + else { + if (option[name] == null) { + option[name] = theme[name]; + } + } + } + } + } + + function initBase(baseOption) { + baseOption = baseOption; + + // Using OPTION_INNER_KEY to mark that this option can not be used outside, + // i.e. `chart.setOption(chart.getModel().option);` is forbiden. + this.option = {}; + this.option[OPTION_INNER_KEY] = 1; + + /** + * @type {Object.>} + * @private + */ + this._componentsMap = {}; + + /** + * Mapping between filtered series list and raw series list. + * key: filtered series indices, value: raw series indices. + * @type {Array.} + * @private + */ + this._seriesIndices = null; + + mergeTheme(baseOption, this._theme.option); + + // TODO Needs clone when merging to the unexisted property + zrUtil.merge(baseOption, globalDefault, false); + + this.mergeOption(baseOption); + } + + /** + * @inner + * @param {Array.|string} types model types + * @return {Object} key: {string} type, value: {Array.} models + */ + function getComponentsByTypes(componentsMap, types) { + if (!zrUtil.isArray(types)) { + types = types ? [types] : []; + } + + var ret = {}; + each(types, function (type) { + ret[type] = (componentsMap[type] || []).slice(); + }); + + return ret; + } + + /** + * @inner + */ + function makeKeyInfo(mainType, mapResult) { + // We use this id to hash component models and view instances + // in echarts. id can be specified by user, or auto generated. + + // The id generation rule ensures new view instance are able + // to mapped to old instance when setOption are called in + // no-merge mode. So we generate model id by name and plus + // type in view id. + + // name can be duplicated among components, which is convenient + // to specify multi components (like series) by one name. + + // Ensure that each id is distinct. + var idMap = {}; + + each(mapResult, function (item, index) { + var existCpt = item.exist; + existCpt && (idMap[existCpt.id] = item); + }); + + each(mapResult, function (item, index) { + var opt = item.option; + + zrUtil.assert( + !opt || opt.id == null || !idMap[opt.id] || idMap[opt.id] === item, + 'id duplicates: ' + (opt && opt.id) + ); + + opt && opt.id != null && (idMap[opt.id] = item); + + // Complete subType + if (isObject(opt)) { + var subType = determineSubType(mainType, opt, item.exist); + item.keyInfo = {mainType: mainType, subType: subType}; + } + }); + + // Make name and id. + each(mapResult, function (item, index) { + var existCpt = item.exist; + var opt = item.option; + var keyInfo = item.keyInfo; + + if (!isObject(opt)) { + return; + } + + // name can be overwitten. Consider case: axis.name = '20km'. + // But id generated by name will not be changed, which affect + // only in that case: setOption with 'not merge mode' and view + // instance will be recreated, which can be accepted. + keyInfo.name = opt.name != null + ? opt.name + '' + : existCpt + ? existCpt.name + : '\0-'; + + if (existCpt) { + keyInfo.id = existCpt.id; + } + else if (opt.id != null) { + keyInfo.id = opt.id + ''; + } + else { + // Consider this situatoin: + // optionA: [{name: 'a'}, {name: 'a'}, {..}] + // optionB [{..}, {name: 'a'}, {name: 'a'}] + // Series with the same name between optionA and optionB + // should be mapped. + var idNum = 0; + do { + keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; + } + while (idMap[keyInfo.id]); + } + + idMap[keyInfo.id] = item; + }); + } + + /** + * @inner + */ + function determineSubType(mainType, newCptOption, existComponent) { + var subType = newCptOption.type + ? newCptOption.type + : existComponent + ? existComponent.subType + // Use determineSubType only when there is no existComponent. + : ComponentModel.determineSubType(mainType, newCptOption); + + // tooltip, markline, markpoint may always has no subType + return subType; + } + + /** + * @inner + */ + function createSeriesIndices(seriesModels) { + return map(seriesModels, function (series) { + return series.componentIndex; + }) || []; + } + + /** + * @inner + */ + function filterBySubType(components, condition) { + // Using hasOwnProperty for restrict. Consider + // subType is undefined in user payload. + return condition.hasOwnProperty('subType') + ? filter(components, function (cpt) { + return cpt.subType === condition.subType; + }) + : components; + } + + /** + * @inner + */ + function assertSeriesInitialized(ecModel) { + // Components that use _seriesIndices should depends on series component, + // which make sure that their initialization is after series. + if (true) { + if (!ecModel._seriesIndices) { + throw new Error('Series has not been initialized yet.'); + } + } + } + + zrUtil.mixin(GlobalModel, __webpack_require__(24)); + + module.exports = GlobalModel; + + +/***/ }, +/* 4 */ +/***/ function(module, exports) { + + /** + * @module zrender/core/util + */ + + + // 用于处理merge时无法遍历Date等对象的问题 + var BUILTIN_OBJECT = { + '[object Function]': 1, + '[object RegExp]': 1, + '[object Date]': 1, + '[object Error]': 1, + '[object CanvasGradient]': 1, + '[object CanvasPattern]': 1, + // In node-canvas Image can be Canvas.Image + '[object Image]': 1 + }; + + var objToString = Object.prototype.toString; + + var arrayProto = Array.prototype; + var nativeForEach = arrayProto.forEach; + var nativeFilter = arrayProto.filter; + var nativeSlice = arrayProto.slice; + var nativeMap = arrayProto.map; + var nativeReduce = arrayProto.reduce; + + /** + * @param {*} source + * @return {*} 拷贝后的新对象 + */ + function clone(source) { + if (typeof source == 'object' && source !== null) { + var result = source; + if (source instanceof Array) { + result = []; + for (var i = 0, len = source.length; i < len; i++) { + result[i] = clone(source[i]); + } + } + else if ( + !isBuildInObject(source) + // 是否为 dom 对象 + && !isDom(source) + ) { + result = {}; + for (var key in source) { + if (source.hasOwnProperty(key)) { + result[key] = clone(source[key]); + } + } + } + + return result; + } + + return source; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} target + * @param {*} source + * @param {boolean} [overwrite=false] + */ + function merge(target, source, overwrite) { + // We should escapse that source is string + // and enter for ... in ... + if (!isObject(source) || !isObject(target)) { + return overwrite ? clone(source) : target; + } + + for (var key in source) { + if (source.hasOwnProperty(key)) { + var targetProp = target[key]; + var sourceProp = source[key]; + + if (isObject(sourceProp) + && isObject(targetProp) + && !isArray(sourceProp) + && !isArray(targetProp) + && !isDom(sourceProp) + && !isDom(targetProp) + && !isBuildInObject(sourceProp) + && !isBuildInObject(targetProp) + ) { + // 如果需要递归覆盖,就递归调用merge + merge(targetProp, sourceProp, overwrite); + } + else if (overwrite || !(key in target)) { + // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况 + // NOTE,在 target[key] 不存在的时候也是直接覆盖 + target[key] = clone(source[key], true); + } + } + } + + return target; + } + + /** + * @param {Array} targetAndSources The first item is target, and the rests are source. + * @param {boolean} [overwrite=false] + * @return {*} target + */ + function mergeAll(targetAndSources, overwrite) { + var result = targetAndSources[0]; + for (var i = 1, len = targetAndSources.length; i < len; i++) { + result = merge(result, targetAndSources[i], overwrite); + } + return result; + } + + /** + * @param {*} target + * @param {*} source + * @memberOf module:zrender/core/util + */ + function extend(target, source) { + for (var key in source) { + if (source.hasOwnProperty(key)) { + target[key] = source[key]; + } + } + return target; + } + + /** + * @param {*} target + * @param {*} source + * @param {boolen} [overlay=false] + * @memberOf module:zrender/core/util + */ + function defaults(target, source, overlay) { + for (var key in source) { + if (source.hasOwnProperty(key) + && (overlay ? source[key] != null : target[key] == null) + ) { + target[key] = source[key]; + } + } + return target; + } + + function createCanvas() { + return document.createElement('canvas'); + } + // FIXME + var _ctx; + function getContext() { + if (!_ctx) { + // Use util.createCanvas instead of createCanvas + // because createCanvas may be overwritten in different environment + _ctx = util.createCanvas().getContext('2d'); + } + return _ctx; + } + + /** + * 查询数组中元素的index + * @memberOf module:zrender/core/util + */ + function indexOf(array, value) { + if (array) { + if (array.indexOf) { + return array.indexOf(value); + } + for (var i = 0, len = array.length; i < len; i++) { + if (array[i] === value) { + return i; + } + } + } + return -1; + } + + /** + * 构造类继承关系 + * + * @memberOf module:zrender/core/util + * @param {Function} clazz 源类 + * @param {Function} baseClazz 基类 + */ + function inherits(clazz, baseClazz) { + var clazzPrototype = clazz.prototype; + function F() {} + F.prototype = baseClazz.prototype; + clazz.prototype = new F(); + + for (var prop in clazzPrototype) { + clazz.prototype[prop] = clazzPrototype[prop]; + } + clazz.prototype.constructor = clazz; + clazz.superClass = baseClazz; + } + + /** + * @memberOf module:zrender/core/util + * @param {Object|Function} target + * @param {Object|Function} sorce + * @param {boolean} overlay + */ + function mixin(target, source, overlay) { + target = 'prototype' in target ? target.prototype : target; + source = 'prototype' in source ? source.prototype : source; + + defaults(target, source, overlay); + } + + /** + * @param {Array|TypedArray} data + */ + function isArrayLike(data) { + if (! data) { + return; + } + if (typeof data == 'string') { + return false; + } + return typeof data.length == 'number'; + } + + /** + * 数组或对象遍历 + * @memberOf module:zrender/core/util + * @param {Object|Array} obj + * @param {Function} cb + * @param {*} [context] + */ + function each(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.forEach && obj.forEach === nativeForEach) { + obj.forEach(cb, context); + } + else if (obj.length === +obj.length) { + for (var i = 0, len = obj.length; i < len; i++) { + cb.call(context, obj[i], i, obj); + } + } + else { + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + cb.call(context, obj[key], key, obj); + } + } + } + } + + /** + * 数组映射 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function map(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.map && obj.map === nativeMap) { + return obj.map(cb, context); + } + else { + var result = []; + for (var i = 0, len = obj.length; i < len; i++) { + result.push(cb.call(context, obj[i], i, obj)); + } + return result; + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {Object} [memo] + * @param {*} [context] + * @return {Array} + */ + function reduce(obj, cb, memo, context) { + if (!(obj && cb)) { + return; + } + if (obj.reduce && obj.reduce === nativeReduce) { + return obj.reduce(cb, memo, context); + } + else { + for (var i = 0, len = obj.length; i < len; i++) { + memo = cb.call(context, memo, obj[i], i, obj); + } + return memo; + } + } + + /** + * 数组过滤 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function filter(obj, cb, context) { + if (!(obj && cb)) { + return; + } + if (obj.filter && obj.filter === nativeFilter) { + return obj.filter(cb, context); + } + else { + var result = []; + for (var i = 0, len = obj.length; i < len; i++) { + if (cb.call(context, obj[i], i, obj)) { + result.push(obj[i]); + } + } + return result; + } + } + + /** + * 数组项查找 + * @memberOf module:zrender/core/util + * @param {Array} obj + * @param {Function} cb + * @param {*} [context] + * @return {Array} + */ + function find(obj, cb, context) { + if (!(obj && cb)) { + return; + } + for (var i = 0, len = obj.length; i < len; i++) { + if (cb.call(context, obj[i], i, obj)) { + return obj[i]; + } + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Function} func + * @param {*} context + * @return {Function} + */ + function bind(func, context) { + var args = nativeSlice.call(arguments, 2); + return function () { + return func.apply(context, args.concat(nativeSlice.call(arguments))); + }; + } + + /** + * @memberOf module:zrender/core/util + * @param {Function} func + * @return {Function} + */ + function curry(func) { + var args = nativeSlice.call(arguments, 1); + return function () { + return func.apply(this, args.concat(nativeSlice.call(arguments))); + }; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isArray(value) { + return objToString.call(value) === '[object Array]'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isFunction(value) { + return typeof value === 'function'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isString(value) { + return objToString.call(value) === '[object String]'; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isObject(value) { + // Avoid a V8 JIT bug in Chrome 19-20. + // See https://code.google.com/p/v8/issues/detail?id=2291 for more details. + var type = typeof value; + return type === 'function' || (!!value && type == 'object'); + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isBuildInObject(value) { + return !!BUILTIN_OBJECT[objToString.call(value)]; + } + + /** + * @memberOf module:zrender/core/util + * @param {*} value + * @return {boolean} + */ + function isDom(value) { + return value && value.nodeType === 1 + && typeof(value.nodeName) == 'string'; + } + + /** + * If value1 is not null, then return value1, otherwise judget rest of values. + * @memberOf module:zrender/core/util + * @return {*} Final value + */ + function retrieve(values) { + for (var i = 0, len = arguments.length; i < len; i++) { + if (arguments[i] != null) { + return arguments[i]; + } + } + } + + /** + * @memberOf module:zrender/core/util + * @param {Array} arr + * @param {number} startIndex + * @param {number} endIndex + * @return {Array} + */ + function slice() { + return Function.call.apply(nativeSlice, arguments); + } + + /** + * @memberOf module:zrender/core/util + * @param {boolean} condition + * @param {string} message + */ + function assert(condition, message) { + if (!condition) { + throw new Error(message); + } + } + + var util = { + inherits: inherits, + mixin: mixin, + clone: clone, + merge: merge, + mergeAll: mergeAll, + extend: extend, + defaults: defaults, + getContext: getContext, + createCanvas: createCanvas, + indexOf: indexOf, + slice: slice, + find: find, + isArrayLike: isArrayLike, + each: each, + map: map, + reduce: reduce, + filter: filter, + bind: bind, + curry: curry, + isArray: isArray, + isString: isString, + isObject: isObject, + isFunction: isFunction, + isBuildInObject: isBuildInObject, + isDom: isDom, + retrieve: retrieve, + assert: assert, + noop: function () {} + }; + module.exports = util; + + + +/***/ }, +/* 5 */ +/***/ function(module, exports, __webpack_require__) { + + + + var formatUtil = __webpack_require__(6); + var nubmerUtil = __webpack_require__(7); + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var modelUtil = {}; + + /** + * If value is not array, then translate it to array. + * @param {*} value + * @return {Array} [value] or value + */ + modelUtil.normalizeToArray = function (value) { + return value instanceof Array + ? value + : value == null + ? [] + : [value]; + }; + + /** + * Sync default option between normal and emphasis like `position` and `show` + * In case some one will write code like + * label: { + * normal: { + * show: false, + * position: 'outside', + * textStyle: { + * fontSize: 18 + * } + * }, + * emphasis: { + * show: true + * } + * } + * @param {Object} opt + * @param {Array.} subOpts + */ + modelUtil.defaultEmphasis = function (opt, subOpts) { + if (opt) { + var emphasisOpt = opt.emphasis = opt.emphasis || {}; + var normalOpt = opt.normal = opt.normal || {}; + + // Default emphasis option from normal + zrUtil.each(subOpts, function (subOptName) { + var val = zrUtil.retrieve(emphasisOpt[subOptName], normalOpt[subOptName]); + if (val != null) { + emphasisOpt[subOptName] = val; + } + }); + } + }; + + modelUtil.LABEL_OPTIONS = ['position', 'show', 'textStyle', 'distance', 'formatter']; + + /** + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method retieves value from data. + * @param {string|number|Date|Array|Object} dataItem + * @return {number|string|Date|Array.} + */ + modelUtil.getDataItemValue = function (dataItem) { + // Performance sensitive. + return dataItem && (dataItem.value == null ? dataItem : dataItem.value); + }; + + /** + * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] + * This helper method determine if dataItem has extra option besides value + * @param {string|number|Date|Array|Object} dataItem + */ + modelUtil.isDataItemOption = function (dataItem) { + return zrUtil.isObject(dataItem) + && !(dataItem instanceof Array); + // // markLine data can be array + // && !(dataItem[0] && zrUtil.isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); + }; + + /** + * This helper method convert value in data. + * @param {string|number|Date} value + * @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'. + */ + modelUtil.converDataValue = function (value, dimInfo) { + // Performance sensitive. + var dimType = dimInfo && dimInfo.type; + if (dimType === 'ordinal') { + return value; + } + + if (dimType === 'time' && !isFinite(value) && value != null && value !== '-') { + value = +nubmerUtil.parseDate(value); + } + + // dimType defaults 'number'. + // If dimType is not ordinal and value is null or undefined or NaN or '-', + // parse to NaN. + return (value == null || value === '') + ? NaN : +value; // If string (like '-'), using '+' parse to NaN + }; + + /** + * Create a model proxy to be used in tooltip for edge data, markLine data, markPoint data. + * @param {module:echarts/data/List} data + * @param {Object} opt + * @param {string} [opt.seriesIndex] + * @param {Object} [opt.name] + * @param {Object} [opt.mainType] + * @param {Object} [opt.subType] + */ + modelUtil.createDataFormatModel = function (data, opt) { + var model = new Model(); + zrUtil.mixin(model, modelUtil.dataFormatMixin); + model.seriesIndex = opt.seriesIndex; + model.name = opt.name || ''; + model.mainType = opt.mainType; + model.subType = opt.subType; + + model.getData = function () { + return data; + }; + return model; + }; + + // PENDING A little ugly + modelUtil.dataFormatMixin = { + /** + * Get params for formatter + * @param {number} dataIndex + * @param {string} [dataType] + * @return {Object} + */ + getDataParams: function (dataIndex, dataType) { + var data = this.getData(dataType); + + var seriesIndex = this.seriesIndex; + var seriesName = this.name; + + var rawValue = this.getRawValue(dataIndex, dataType); + var rawDataIndex = data.getRawIndex(dataIndex); + var name = data.getName(dataIndex, true); + var itemOpt = data.getRawDataItem(dataIndex); + + return { + componentType: this.mainType, + componentSubType: this.subType, + seriesType: this.mainType === 'series' ? this.subType : null, + seriesIndex: seriesIndex, + seriesName: seriesName, + name: name, + dataIndex: rawDataIndex, + data: itemOpt, + dataType: dataType, + value: rawValue, + color: data.getItemVisual(dataIndex, 'color'), + + // Param name list for mapping `a`, `b`, `c`, `d`, `e` + $vars: ['seriesName', 'name', 'value'] + }; + }, + + /** + * Format label + * @param {number} dataIndex + * @param {string} [status='normal'] 'normal' or 'emphasis' + * @param {string} [dataType] + * @param {number} [dimIndex] + * @return {string} + */ + getFormattedLabel: function (dataIndex, status, dataType, dimIndex) { + status = status || 'normal'; + var data = this.getData(dataType); + var itemModel = data.getItemModel(dataIndex); + + var params = this.getDataParams(dataIndex, dataType); + if (dimIndex != null && (params.value instanceof Array)) { + params.value = params.value[dimIndex]; + } + + var formatter = itemModel.get(['label', status, 'formatter']); + + if (typeof formatter === 'function') { + params.status = status; + return formatter(params); + } + else if (typeof formatter === 'string') { + return formatUtil.formatTpl(formatter, params); + } + }, + + /** + * Get raw value in option + * @param {number} idx + * @param {string} [dataType] + * @return {Object} + */ + getRawValue: function (idx, dataType) { + var data = this.getData(dataType); + var dataItem = data.getRawDataItem(idx); + if (dataItem != null) { + return (zrUtil.isObject(dataItem) && !(dataItem instanceof Array)) + ? dataItem.value : dataItem; + } + }, + + /** + * Should be implemented. + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {number} [dataType] + * @return {string} tooltip string + */ + formatTooltip: zrUtil.noop + }; + + /** + * Mapping to exists for merge. + * + * @public + * @param {Array.|Array.} exists + * @param {Object|Array.} newCptOptions + * @return {Array.} Result, like [{exist: ..., option: ...}, {}], + * which order is the same as exists. + */ + modelUtil.mappingToExists = function (exists, newCptOptions) { + // Mapping by the order by original option (but not order of + // new option) in merge mode. Because we should ensure + // some specified index (like xAxisIndex) is consistent with + // original option, which is easy to understand, espatially in + // media query. And in most case, merge option is used to + // update partial option but not be expected to change order. + newCptOptions = (newCptOptions || []).slice(); + + var result = zrUtil.map(exists || [], function (obj, index) { + return {exist: obj}; + }); + + // Mapping by id or name if specified. + zrUtil.each(newCptOptions, function (cptOption, index) { + if (!zrUtil.isObject(cptOption)) { + return; + } + + // id has highest priority. + for (var i = 0; i < result.length; i++) { + if (!result[i].option // Consider name: two map to one. + && cptOption.id != null + && result[i].exist.id === cptOption.id + '' + ) { + result[i].option = cptOption; + newCptOptions[index] = null; + return; + } + } + + for (var i = 0; i < result.length; i++) { + var exist = result[i].exist; + if (!result[i].option // Consider name: two map to one. + // Can not match when both ids exist but different. + && (exist.id == null || cptOption.id == null) + && cptOption.name != null + && !modelUtil.isIdInner(cptOption) + && !modelUtil.isIdInner(exist) + && exist.name === cptOption.name + '' + ) { + result[i].option = cptOption; + newCptOptions[index] = null; + return; + } + } + }); + + // Otherwise mapping by index. + zrUtil.each(newCptOptions, function (cptOption, index) { + if (!zrUtil.isObject(cptOption)) { + return; + } + + var i = 0; + for (; i < result.length; i++) { + var exist = result[i].exist; + if (!result[i].option + && !modelUtil.isIdInner(exist) + // Caution: + // Do not overwrite id. But name can be overwritten, + // because axis use name as 'show label text'. + // 'exist' always has id and name and we dont + // need to check it. + && cptOption.id == null + ) { + result[i].option = cptOption; + break; + } + } + + if (i >= result.length) { + result.push({option: cptOption}); + } + }); + + return result; + }; + + /** + * @public + * @param {Object} cptOption + * @return {boolean} + */ + modelUtil.isIdInner = function (cptOption) { + return zrUtil.isObject(cptOption) + && cptOption.id + && (cptOption.id + '').indexOf('\0_ec_\0') === 0; + }; + + /** + * A helper for removing duplicate items between batchA and batchB, + * and in themselves, and categorize by series. + * + * @param {Array.} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @param {Array.} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...] + * @return {Array., Array.>} result: [resultBatchA, resultBatchB] + */ + modelUtil.compressBatches = function (batchA, batchB) { + var mapA = {}; + var mapB = {}; + + makeMap(batchA || [], mapA); + makeMap(batchB || [], mapB, mapA); + + return [mapToArray(mapA), mapToArray(mapB)]; + + function makeMap(sourceBatch, map, otherMap) { + for (var i = 0, len = sourceBatch.length; i < len; i++) { + var seriesId = sourceBatch[i].seriesId; + var dataIndices = modelUtil.normalizeToArray(sourceBatch[i].dataIndex); + var otherDataIndices = otherMap && otherMap[seriesId]; + + for (var j = 0, lenj = dataIndices.length; j < lenj; j++) { + var dataIndex = dataIndices[j]; + + if (otherDataIndices && otherDataIndices[dataIndex]) { + otherDataIndices[dataIndex] = null; + } + else { + (map[seriesId] || (map[seriesId] = {}))[dataIndex] = 1; + } + } + } + } + + function mapToArray(map, isData) { + var result = []; + for (var i in map) { + if (map.hasOwnProperty(i) && map[i] != null) { + if (isData) { + result.push(+i); + } + else { + var dataIndices = mapToArray(map[i], true); + dataIndices.length && result.push({seriesId: i, dataIndex: dataIndices}); + } + } + } + return result; + } + }; + + module.exports = modelUtil; + + +/***/ }, +/* 6 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var textContain = __webpack_require__(8); + + var formatUtil = {}; + /** + * 每三位默认加,格式化 + * @type {string|number} x + */ + formatUtil.addCommas = function (x) { + if (isNaN(x)) { + return '-'; + } + x = (x + '').split('.'); + return x[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g,'$1,') + + (x.length > 1 ? ('.' + x[1]) : ''); + }; + + /** + * @param {string} str + * @return {string} str + */ + formatUtil.toCamelCase = function (str) { + return str.toLowerCase().replace(/-(.)/g, function(match, group1) { + return group1.toUpperCase(); + }); + }; + + /** + * Normalize css liked array configuration + * e.g. + * 3 => [3, 3, 3, 3] + * [4, 2] => [4, 2, 4, 2] + * [4, 3, 2] => [4, 3, 2, 3] + * @param {number|Array.} val + */ + formatUtil.normalizeCssArray = function (val) { + var len = val.length; + if (typeof (val) === 'number') { + return [val, val, val, val]; + } + else if (len === 2) { + // vertical | horizontal + return [val[0], val[1], val[0], val[1]]; + } + else if (len === 3) { + // top | horizontal | bottom + return [val[0], val[1], val[2], val[1]]; + } + return val; + }; + + formatUtil.encodeHTML = function (source) { + return String(source) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }; + + var TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; + + var wrapVar = function (varName, seriesIdx) { + return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; + }; + + /** + * Template formatter + * @param {string} tpl + * @param {Array.|Object} paramsList + * @return {string} + */ + formatUtil.formatTpl = function (tpl, paramsList) { + if (!zrUtil.isArray(paramsList)) { + paramsList = [paramsList]; + } + var seriesLen = paramsList.length; + if (!seriesLen) { + return ''; + } + + var $vars = paramsList[0].$vars || []; + for (var i = 0; i < $vars.length; i++) { + var alias = TPL_VAR_ALIAS[i]; + tpl = tpl.replace(wrapVar(alias), wrapVar(alias, 0)); + } + for (var seriesIdx = 0; seriesIdx < seriesLen; seriesIdx++) { + for (var k = 0; k < $vars.length; k++) { + tpl = tpl.replace( + wrapVar(TPL_VAR_ALIAS[k], seriesIdx), + paramsList[seriesIdx][$vars[k]] + ); + } + } + + return tpl; + }; + + + /** + * @param {string} str + * @return {string} + * @inner + */ + var s2d = function (str) { + return str < 10 ? ('0' + str) : str; + }; + + /** + * ISO Date format + * @param {string} tpl + * @param {number} value + * @inner + */ + formatUtil.formatTime = function (tpl, value) { + if (tpl === 'week' + || tpl === 'month' + || tpl === 'quarter' + || tpl === 'half-year' + || tpl === 'year' + ) { + tpl = 'MM-dd\nyyyy'; + } + + var date = numberUtil.parseDate(value); + var y = date.getFullYear(); + var M = date.getMonth() + 1; + var d = date.getDate(); + var h = date.getHours(); + var m = date.getMinutes(); + var s = date.getSeconds(); + + tpl = tpl.replace('MM', s2d(M)) + .toLowerCase() + .replace('yyyy', y) + .replace('yy', y % 100) + .replace('dd', s2d(d)) + .replace('d', d) + .replace('hh', s2d(h)) + .replace('h', h) + .replace('mm', s2d(m)) + .replace('m', m) + .replace('ss', s2d(s)) + .replace('s', s); + + return tpl; + }; + + /** + * Capital first + * @param {string} str + * @return {string} + */ + formatUtil.capitalFirst = function (str) { + return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; + }; + + formatUtil.truncateText = textContain.truncateText; + + module.exports = formatUtil; + + +/***/ }, +/* 7 */ +/***/ function(module, exports) { + + /** + * 数值处理模块 + * @module echarts/util/number + */ + + + + var number = {}; + + var RADIAN_EPSILON = 1e-4; + + function _trim(str) { + return str.replace(/^\s+/, '').replace(/\s+$/, ''); + } + + /** + * Linear mapping a value from domain to range + * @memberOf module:echarts/util/number + * @param {(number|Array.)} val + * @param {Array.} domain Domain extent domain[0] can be bigger than domain[1] + * @param {Array.} range Range extent range[0] can be bigger than range[1] + * @param {boolean} clamp + * @return {(number|Array.} + */ + number.linearMap = function (val, domain, range, clamp) { + var subDomain = domain[1] - domain[0]; + var subRange = range[1] - range[0]; + + if (subDomain === 0) { + return subRange === 0 + ? range[0] + : (range[0] + range[1]) / 2; + } + + // Avoid accuracy problem in edge, such as + // 146.39 - 62.83 === 83.55999999999999. + // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError + // It is a little verbose for efficiency considering this method + // is a hotspot. + if (clamp) { + if (subDomain > 0) { + if (val <= domain[0]) { + return range[0]; + } + else if (val >= domain[1]) { + return range[1]; + } + } + else { + if (val >= domain[0]) { + return range[0]; + } + else if (val <= domain[1]) { + return range[1]; + } + } + } + else { + if (val === domain[0]) { + return range[0]; + } + if (val === domain[1]) { + return range[1]; + } + } + + return (val - domain[0]) / subDomain * subRange + range[0]; + }; + + /** + * Convert a percent string to absolute number. + * Returns NaN if percent is not a valid string or number + * @memberOf module:echarts/util/number + * @param {string|number} percent + * @param {number} all + * @return {number} + */ + number.parsePercent = function(percent, all) { + switch (percent) { + case 'center': + case 'middle': + percent = '50%'; + break; + case 'left': + case 'top': + percent = '0%'; + break; + case 'right': + case 'bottom': + percent = '100%'; + break; + } + if (typeof percent === 'string') { + if (_trim(percent).match(/%$/)) { + return parseFloat(percent) / 100 * all; + } + + return parseFloat(percent); + } + + return percent == null ? NaN : +percent; + }; + + /** + * Fix rounding error of float numbers + * @param {number} x + * @return {number} + */ + number.round = function (x, precision) { + if (precision == null) { + precision = 10; + } + // PENDING + return +(+x).toFixed(precision); + }; + + number.asc = function (arr) { + arr.sort(function (a, b) { + return a - b; + }); + return arr; + }; + + /** + * Get precision + * @param {number} val + */ + number.getPrecision = function (val) { + val = +val; + if (isNaN(val)) { + return 0; + } + // It is much faster than methods converting number to string as follows + // var tmp = val.toString(); + // return tmp.length - 1 - tmp.indexOf('.'); + // especially when precision is low + var e = 1; + var count = 0; + while (Math.round(val * e) / e !== val) { + e *= 10; + count++; + } + return count; + }; + + number.getPrecisionSafe = function (val) { + var str = val.toString(); + var dotIndex = str.indexOf('.'); + if (dotIndex < 0) { + return 0; + } + return str.length - 1 - dotIndex; + }; + + /** + * @param {Array.} dataExtent + * @param {Array.} pixelExtent + * @return {number} precision + */ + number.getPixelPrecision = function (dataExtent, pixelExtent) { + var log = Math.log; + var LN10 = Math.LN10; + var dataQuantity = Math.floor(log(dataExtent[1] - dataExtent[0]) / LN10); + var sizeQuantity = Math.round(log(Math.abs(pixelExtent[1] - pixelExtent[0])) / LN10); + return Math.max( + -dataQuantity + sizeQuantity, + 0 + ); + }; + + // Number.MAX_SAFE_INTEGER, ie do not support. + number.MAX_SAFE_INTEGER = 9007199254740991; + + /** + * To 0 - 2 * PI, considering negative radian. + * @param {number} radian + * @return {number} + */ + number.remRadian = function (radian) { + var pi2 = Math.PI * 2; + return (radian % pi2 + pi2) % pi2; + }; + + /** + * @param {type} radian + * @return {boolean} + */ + number.isRadianAroundZero = function (val) { + return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; + }; + + /** + * @param {string|Date|number} value + * @return {Date} date + */ + number.parseDate = function (value) { + if (value instanceof Date) { + return value; + } + else if (typeof value === 'string') { + // Treat as ISO format. See issue #3623 + var ret = new Date(value); + if (isNaN(+ret)) { + // FIXME new Date('1970-01-01') is UTC, new Date('1970/01/01') is local + ret = new Date(new Date(value.replace(/-/g, '/')) - new Date('1970/01/01')); + } + return ret; + } + + return new Date(Math.round(value)); + }; + + /** + * Quantity of a number. e.g. 0.1, 1, 10, 100 + * @param {number} val + * @return {number} + */ + number.quantity = function (val) { + return Math.pow(10, Math.floor(Math.log(val) / Math.LN10)); + }; + + // "Nice Numbers for Graph Labels" of Graphic Gems + /** + * find a “nice” number approximately equal to x. Round the number if round = true, take ceiling if round = false + * The primary observation is that the “nicest” numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers. + * @param {number} val + * @param {boolean} round + * @return {number} + */ + number.nice = function (val, round) { + var exp10 = number.quantity(val); + var f = val / exp10; // between 1 and 10 + var nf; + if (round) { + if (f < 1.5) { nf = 1; } + else if (f < 2.5) { nf = 2; } + else if (f < 4) { nf = 3; } + else if (f < 7) { nf = 5; } + else { nf = 10; } + } + else { + if (f < 1) { nf = 1; } + else if (f < 2) { nf = 2; } + else if (f < 3) { nf = 3; } + else if (f < 5) { nf = 5; } + else { nf = 10; } + } + return nf * exp10; + }; + + module.exports = number; + + +/***/ }, +/* 8 */ +/***/ function(module, exports, __webpack_require__) { + + + + var textWidthCache = {}; + var textWidthCacheCounter = 0; + var TEXT_CACHE_MAX = 5000; + + var util = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var retrieve = util.retrieve; + + function getTextWidth(text, textFont) { + var key = text + ':' + textFont; + if (textWidthCache[key]) { + return textWidthCache[key]; + } + + var textLines = (text + '').split('\n'); + var width = 0; + + for (var i = 0, l = textLines.length; i < l; i++) { + // measureText 可以被覆盖以兼容不支持 Canvas 的环境 + width = Math.max(textContain.measureText(textLines[i], textFont).width, width); + } + + if (textWidthCacheCounter > TEXT_CACHE_MAX) { + textWidthCacheCounter = 0; + textWidthCache = {}; + } + textWidthCacheCounter++; + textWidthCache[key] = width; + + return width; + } + + function getTextRect(text, textFont, textAlign, textBaseline) { + var textLineLen = ((text || '') + '').split('\n').length; + + var width = getTextWidth(text, textFont); + // FIXME 高度计算比较粗暴 + var lineHeight = getTextWidth('国', textFont); + var height = textLineLen * lineHeight; + + var rect = new BoundingRect(0, 0, width, height); + // Text has a special line height property + rect.lineHeight = lineHeight; + + switch (textBaseline) { + case 'bottom': + case 'alphabetic': + rect.y -= lineHeight; + break; + case 'middle': + rect.y -= lineHeight / 2; + break; + // case 'hanging': + // case 'top': + } + + // FIXME Right to left language + switch (textAlign) { + case 'end': + case 'right': + rect.x -= rect.width; + break; + case 'center': + rect.x -= rect.width / 2; + break; + // case 'start': + // case 'left': + } + + return rect; + } + + function adjustTextPositionOnRect(textPosition, rect, textRect, distance) { + + var x = rect.x; + var y = rect.y; + + var height = rect.height; + var width = rect.width; + + var textHeight = textRect.height; + + var halfHeight = height / 2 - textHeight / 2; + + var textAlign = 'left'; + + switch (textPosition) { + case 'left': + x -= distance; + y += halfHeight; + textAlign = 'right'; + break; + case 'right': + x += distance + width; + y += halfHeight; + textAlign = 'left'; + break; + case 'top': + x += width / 2; + y -= distance + textHeight; + textAlign = 'center'; + break; + case 'bottom': + x += width / 2; + y += height + distance; + textAlign = 'center'; + break; + case 'inside': + x += width / 2; + y += halfHeight; + textAlign = 'center'; + break; + case 'insideLeft': + x += distance; + y += halfHeight; + textAlign = 'left'; + break; + case 'insideRight': + x += width - distance; + y += halfHeight; + textAlign = 'right'; + break; + case 'insideTop': + x += width / 2; + y += distance; + textAlign = 'center'; + break; + case 'insideBottom': + x += width / 2; + y += height - textHeight - distance; + textAlign = 'center'; + break; + case 'insideTopLeft': + x += distance; + y += distance; + textAlign = 'left'; + break; + case 'insideTopRight': + x += width - distance; + y += distance; + textAlign = 'right'; + break; + case 'insideBottomLeft': + x += distance; + y += height - textHeight - distance; + break; + case 'insideBottomRight': + x += width - distance; + y += height - textHeight - distance; + textAlign = 'right'; + break; + } + + return { + x: x, + y: y, + textAlign: textAlign, + textBaseline: 'top' + }; + } + + /** + * Show ellipsis if overflow. + * + * @param {string} text + * @param {string} containerWidth + * @param {string} textFont + * @param {number} [ellipsis='...'] + * @param {Object} [options] + * @param {number} [options.maxIterations=3] + * @param {number} [options.minChar=0] If truncate result are less + * then minChar, ellipsis will not show, which is + * better for user hint in some cases. + * @param {number} [options.placeholder=''] When all truncated, use the placeholder. + * @return {string} + */ + function truncateText(text, containerWidth, textFont, ellipsis, options) { + if (!containerWidth) { + return ''; + } + + options = options || {}; + + ellipsis = retrieve(ellipsis, '...'); + var maxIterations = retrieve(options.maxIterations, 2); + var minChar = retrieve(options.minChar, 0); + // FIXME + // Other languages? + var cnCharWidth = getTextWidth('国', textFont); + // FIXME + // Consider proportional font? + var ascCharWidth = getTextWidth('a', textFont); + var placeholder = retrieve(options.placeholder, ''); + + // Example 1: minChar: 3, text: 'asdfzxcv', truncate result: 'asdf', but not: 'a...'. + // Example 2: minChar: 3, text: '维度', truncate result: '维', but not: '...'. + var contentWidth = containerWidth = Math.max(0, containerWidth - 1); // Reserve some gap. + for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) { + contentWidth -= ascCharWidth; + } + + var ellipsisWidth = getTextWidth(ellipsis); + if (ellipsisWidth > contentWidth) { + ellipsis = ''; + ellipsisWidth = 0; + } + + contentWidth = containerWidth - ellipsisWidth; + + var textLines = (text + '').split('\n'); + + for (var i = 0, len = textLines.length; i < len; i++) { + var textLine = textLines[i]; + var lineWidth = getTextWidth(textLine, textFont); + + if (lineWidth <= containerWidth) { + continue; + } + + for (var j = 0;; j++) { + if (lineWidth <= contentWidth || j >= maxIterations) { + textLine += ellipsis; + break; + } + + var subLength = j === 0 + ? estimateLength(textLine, contentWidth, ascCharWidth, cnCharWidth) + : lineWidth > 0 + ? Math.floor(textLine.length * contentWidth / lineWidth) + : 0; + + textLine = textLine.substr(0, subLength); + lineWidth = getTextWidth(textLine, textFont); + } + + if (textLine === '') { + textLine = placeholder; + } + + textLines[i] = textLine; + } + + return textLines.join('\n'); + } + + function estimateLength(text, contentWidth, ascCharWidth, cnCharWidth) { + var width = 0; + var i = 0; + for (var len = text.length; i < len && width < contentWidth; i++) { + var charCode = text.charCodeAt(i); + width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth; + } + return i; + } + + var textContain = { + + getWidth: getTextWidth, + + getBoundingRect: getTextRect, + + adjustTextPositionOnRect: adjustTextPositionOnRect, + + truncateText: truncateText, + + measureText: function (text, textFont) { + var ctx = util.getContext(); + ctx.font = textFont || '12px sans-serif'; + return ctx.measureText(text); + } + }; + + module.exports = textContain; + + +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module echarts/core/BoundingRect + */ + + + var vec2 = __webpack_require__(10); + var matrix = __webpack_require__(11); + + var v2ApplyTransform = vec2.applyTransform; + var mathMin = Math.min; + var mathAbs = Math.abs; + var mathMax = Math.max; + /** + * @alias module:echarts/core/BoundingRect + */ + function BoundingRect(x, y, width, height) { + /** + * @type {number} + */ + this.x = x; + /** + * @type {number} + */ + this.y = y; + /** + * @type {number} + */ + this.width = width; + /** + * @type {number} + */ + this.height = height; + } + + BoundingRect.prototype = { + + constructor: BoundingRect, + + /** + * @param {module:echarts/core/BoundingRect} other + */ + union: function (other) { + var x = mathMin(other.x, this.x); + var y = mathMin(other.y, this.y); + + this.width = mathMax( + other.x + other.width, + this.x + this.width + ) - x; + this.height = mathMax( + other.y + other.height, + this.y + this.height + ) - y; + this.x = x; + this.y = y; + }, + + /** + * @param {Array.} m + * @methods + */ + applyTransform: (function () { + var min = []; + var max = []; + return function (m) { + // In case usage like this + // el.getBoundingRect().applyTransform(el.transform) + // And element has no transform + if (!m) { + return; + } + min[0] = this.x; + min[1] = this.y; + max[0] = this.x + this.width; + max[1] = this.y + this.height; + + v2ApplyTransform(min, min, m); + v2ApplyTransform(max, max, m); + + this.x = mathMin(min[0], max[0]); + this.y = mathMin(min[1], max[1]); + this.width = mathAbs(max[0] - min[0]); + this.height = mathAbs(max[1] - min[1]); + }; + })(), + + /** + * Calculate matrix of transforming from self to target rect + * @param {module:zrender/core/BoundingRect} b + * @return {Array.} + */ + calculateTransform: function (b) { + var a = this; + var sx = b.width / a.width; + var sy = b.height / a.height; + + var m = matrix.create(); + + // 矩阵右乘 + matrix.translate(m, m, [-a.x, -a.y]); + matrix.scale(m, m, [sx, sy]); + matrix.translate(m, m, [b.x, b.y]); + + return m; + }, + + /** + * @param {(module:echarts/core/BoundingRect|Object)} b + * @return {boolean} + */ + intersect: function (b) { + var a = this; + var ax0 = a.x; + var ax1 = a.x + a.width; + var ay0 = a.y; + var ay1 = a.y + a.height; + + var bx0 = b.x; + var bx1 = b.x + b.width; + var by0 = b.y; + var by1 = b.y + b.height; + + return ! (ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); + }, + + contain: function (x, y) { + var rect = this; + return x >= rect.x + && x <= (rect.x + rect.width) + && y >= rect.y + && y <= (rect.y + rect.height); + }, + + /** + * @return {module:echarts/core/BoundingRect} + */ + clone: function () { + return new BoundingRect(this.x, this.y, this.width, this.height); + }, + + /** + * Copy from another rect + */ + copy: function (other) { + this.x = other.x; + this.y = other.y; + this.width = other.width; + this.height = other.height; + } + }; + + module.exports = BoundingRect; + + +/***/ }, +/* 10 */ +/***/ function(module, exports) { + + + var ArrayCtor = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + + /** + * @typedef {Float32Array|Array.} Vector2 + */ + /** + * 二维向量类 + * @exports zrender/tool/vector + */ + var vector = { + /** + * 创建一个向量 + * @param {number} [x=0] + * @param {number} [y=0] + * @return {Vector2} + */ + create: function (x, y) { + var out = new ArrayCtor(2); + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + out[0] = x; + out[1] = y; + return out; + }, + + /** + * 复制向量数据 + * @param {Vector2} out + * @param {Vector2} v + * @return {Vector2} + */ + copy: function (out, v) { + out[0] = v[0]; + out[1] = v[1]; + return out; + }, + + /** + * 克隆一个向量 + * @param {Vector2} v + * @return {Vector2} + */ + clone: function (v) { + var out = new ArrayCtor(2); + out[0] = v[0]; + out[1] = v[1]; + return out; + }, + + /** + * 设置向量的两个项 + * @param {Vector2} out + * @param {number} a + * @param {number} b + * @return {Vector2} 结果 + */ + set: function (out, a, b) { + out[0] = a; + out[1] = b; + return out; + }, + + /** + * 向量相加 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + add: function (out, v1, v2) { + out[0] = v1[0] + v2[0]; + out[1] = v1[1] + v2[1]; + return out; + }, + + /** + * 向量缩放后相加 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + * @param {number} a + */ + scaleAndAdd: function (out, v1, v2, a) { + out[0] = v1[0] + v2[0] * a; + out[1] = v1[1] + v2[1] * a; + return out; + }, + + /** + * 向量相减 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + sub: function (out, v1, v2) { + out[0] = v1[0] - v2[0]; + out[1] = v1[1] - v2[1]; + return out; + }, + + /** + * 向量长度 + * @param {Vector2} v + * @return {number} + */ + len: function (v) { + return Math.sqrt(this.lenSquare(v)); + }, + + /** + * 向量长度平方 + * @param {Vector2} v + * @return {number} + */ + lenSquare: function (v) { + return v[0] * v[0] + v[1] * v[1]; + }, + + /** + * 向量乘法 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + mul: function (out, v1, v2) { + out[0] = v1[0] * v2[0]; + out[1] = v1[1] * v2[1]; + return out; + }, + + /** + * 向量除法 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + div: function (out, v1, v2) { + out[0] = v1[0] / v2[0]; + out[1] = v1[1] / v2[1]; + return out; + }, + + /** + * 向量点乘 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + dot: function (v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1]; + }, + + /** + * 向量缩放 + * @param {Vector2} out + * @param {Vector2} v + * @param {number} s + */ + scale: function (out, v, s) { + out[0] = v[0] * s; + out[1] = v[1] * s; + return out; + }, + + /** + * 向量归一化 + * @param {Vector2} out + * @param {Vector2} v + */ + normalize: function (out, v) { + var d = vector.len(v); + if (d === 0) { + out[0] = 0; + out[1] = 0; + } + else { + out[0] = v[0] / d; + out[1] = v[1] / d; + } + return out; + }, + + /** + * 计算向量间距离 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + distance: function (v1, v2) { + return Math.sqrt( + (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]) + ); + }, + + /** + * 向量距离平方 + * @param {Vector2} v1 + * @param {Vector2} v2 + * @return {number} + */ + distanceSquare: function (v1, v2) { + return (v1[0] - v2[0]) * (v1[0] - v2[0]) + + (v1[1] - v2[1]) * (v1[1] - v2[1]); + }, + + /** + * 求负向量 + * @param {Vector2} out + * @param {Vector2} v + */ + negate: function (out, v) { + out[0] = -v[0]; + out[1] = -v[1]; + return out; + }, + + /** + * 插值两个点 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + * @param {number} t + */ + lerp: function (out, v1, v2, t) { + out[0] = v1[0] + t * (v2[0] - v1[0]); + out[1] = v1[1] + t * (v2[1] - v1[1]); + return out; + }, + + /** + * 矩阵左乘向量 + * @param {Vector2} out + * @param {Vector2} v + * @param {Vector2} m + */ + applyTransform: function (out, v, m) { + var x = v[0]; + var y = v[1]; + out[0] = m[0] * x + m[2] * y + m[4]; + out[1] = m[1] * x + m[3] * y + m[5]; + return out; + }, + /** + * 求两个向量最小值 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + min: function (out, v1, v2) { + out[0] = Math.min(v1[0], v2[0]); + out[1] = Math.min(v1[1], v2[1]); + return out; + }, + /** + * 求两个向量最大值 + * @param {Vector2} out + * @param {Vector2} v1 + * @param {Vector2} v2 + */ + max: function (out, v1, v2) { + out[0] = Math.max(v1[0], v2[0]); + out[1] = Math.max(v1[1], v2[1]); + return out; + } + }; + + vector.length = vector.len; + vector.lengthSquare = vector.lenSquare; + vector.dist = vector.distance; + vector.distSquare = vector.distanceSquare; + + module.exports = vector; + + + +/***/ }, +/* 11 */ +/***/ function(module, exports) { + + + var ArrayCtor = typeof Float32Array === 'undefined' + ? Array + : Float32Array; + /** + * 3x2矩阵操作类 + * @exports zrender/tool/matrix + */ + var matrix = { + /** + * 创建一个单位矩阵 + * @return {Float32Array|Array.} + */ + create : function() { + var out = new ArrayCtor(6); + matrix.identity(out); + + return out; + }, + /** + * 设置矩阵为单位矩阵 + * @param {Float32Array|Array.} out + */ + identity : function(out) { + out[0] = 1; + out[1] = 0; + out[2] = 0; + out[3] = 1; + out[4] = 0; + out[5] = 0; + return out; + }, + /** + * 复制矩阵 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} m + */ + copy: function(out, m) { + out[0] = m[0]; + out[1] = m[1]; + out[2] = m[2]; + out[3] = m[3]; + out[4] = m[4]; + out[5] = m[5]; + return out; + }, + /** + * 矩阵相乘 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} m1 + * @param {Float32Array|Array.} m2 + */ + mul : function (out, m1, m2) { + // Consider matrix.mul(m, m2, m); + // where out is the same as m2. + // So use temp variable to escape error. + var out0 = m1[0] * m2[0] + m1[2] * m2[1]; + var out1 = m1[1] * m2[0] + m1[3] * m2[1]; + var out2 = m1[0] * m2[2] + m1[2] * m2[3]; + var out3 = m1[1] * m2[2] + m1[3] * m2[3]; + var out4 = m1[0] * m2[4] + m1[2] * m2[5] + m1[4]; + var out5 = m1[1] * m2[4] + m1[3] * m2[5] + m1[5]; + out[0] = out0; + out[1] = out1; + out[2] = out2; + out[3] = out3; + out[4] = out4; + out[5] = out5; + return out; + }, + /** + * 平移变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {Float32Array|Array.} v + */ + translate : function(out, a, v) { + out[0] = a[0]; + out[1] = a[1]; + out[2] = a[2]; + out[3] = a[3]; + out[4] = a[4] + v[0]; + out[5] = a[5] + v[1]; + return out; + }, + /** + * 旋转变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {number} rad + */ + rotate : function(out, a, rad) { + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + var st = Math.sin(rad); + var ct = Math.cos(rad); + + out[0] = aa * ct + ab * st; + out[1] = -aa * st + ab * ct; + out[2] = ac * ct + ad * st; + out[3] = -ac * st + ct * ad; + out[4] = ct * atx + st * aty; + out[5] = ct * aty - st * atx; + return out; + }, + /** + * 缩放变换 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + * @param {Float32Array|Array.} v + */ + scale : function(out, a, v) { + var vx = v[0]; + var vy = v[1]; + out[0] = a[0] * vx; + out[1] = a[1] * vy; + out[2] = a[2] * vx; + out[3] = a[3] * vy; + out[4] = a[4] * vx; + out[5] = a[5] * vy; + return out; + }, + /** + * 求逆矩阵 + * @param {Float32Array|Array.} out + * @param {Float32Array|Array.} a + */ + invert : function(out, a) { + + var aa = a[0]; + var ac = a[2]; + var atx = a[4]; + var ab = a[1]; + var ad = a[3]; + var aty = a[5]; + + var det = aa * ad - ab * ac; + if (!det) { + return null; + } + det = 1.0 / det; + + out[0] = ad * det; + out[1] = -ab * det; + out[2] = -ac * det; + out[3] = aa * det; + out[4] = (ac * aty - ad * atx) * det; + out[5] = (ab * atx - aa * aty) * det; + return out; + } + }; + + module.exports = matrix; + + + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/model/Model + */ + + + var zrUtil = __webpack_require__(4); + var clazzUtil = __webpack_require__(13); + + /** + * @alias module:echarts/model/Model + * @constructor + * @param {Object} option + * @param {module:echarts/model/Model} [parentModel] + * @param {module:echarts/model/Global} [ecModel] + */ + function Model(option, parentModel, ecModel) { + /** + * @type {module:echarts/model/Model} + * @readOnly + */ + this.parentModel = parentModel; + + /** + * @type {module:echarts/model/Global} + * @readOnly + */ + this.ecModel = ecModel; + + /** + * @type {Object} + * @protected + */ + this.option = option; + + // Simple optimization + // if (this.init) { + // if (arguments.length <= 4) { + // this.init(option, parentModel, ecModel, extraOpt); + // } + // else { + // this.init.apply(this, arguments); + // } + // } + } + + Model.prototype = { + + constructor: Model, + + /** + * Model 的初始化函数 + * @param {Object} option + */ + init: null, + + /** + * 从新的 Option merge + */ + mergeOption: function (option) { + zrUtil.merge(this.option, option, true); + }, + + /** + * @param {string} path + * @param {boolean} [ignoreParent=false] + * @return {*} + */ + get: function (path, ignoreParent) { + if (!path) { + return this.option; + } + + if (typeof path === 'string') { + path = path.split('.'); + } + + var obj = this.option; + var parentModel = this.parentModel; + for (var i = 0; i < path.length; i++) { + // Ignore empty + if (!path[i]) { + continue; + } + // obj could be number/string/... (like 0) + obj = (obj && typeof obj === 'object') ? obj[path[i]] : null; + if (obj == null) { + break; + } + } + if (obj == null && parentModel && !ignoreParent) { + obj = parentModel.get(path); + } + return obj; + }, + + /** + * @param {string} key + * @param {boolean} [ignoreParent=false] + * @return {*} + */ + getShallow: function (key, ignoreParent) { + var option = this.option; + + var val = option == null ? option : option[key]; + var parentModel = this.parentModel; + if (val == null && parentModel && !ignoreParent) { + val = parentModel.getShallow(key); + } + return val; + }, + + /** + * @param {string} path + * @param {module:echarts/model/Model} [parentModel] + * @return {module:echarts/model/Model} + */ + getModel: function (path, parentModel) { + var obj = this.get(path, true); + var thisParentModel = this.parentModel; + var model = new Model( + obj, parentModel || (thisParentModel && thisParentModel.getModel(path)), + this.ecModel + ); + return model; + }, + + /** + * If model has option + */ + isEmpty: function () { + return this.option == null; + }, + + restoreData: function () {}, + + // Pending + clone: function () { + var Ctor = this.constructor; + return new Ctor(zrUtil.clone(this.option)); + }, + + setReadOnly: function (properties) { + clazzUtil.setReadOnly(this, properties); + } + }; + + // Enable Model.extend. + clazzUtil.enableClassExtend(Model); + + var mixin = zrUtil.mixin; + mixin(Model, __webpack_require__(14)); + mixin(Model, __webpack_require__(16)); + mixin(Model, __webpack_require__(17)); + mixin(Model, __webpack_require__(18)); + + module.exports = Model; + + +/***/ }, +/* 13 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var clazz = {}; + + var TYPE_DELIMITER = '.'; + var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; + /** + * @public + */ + var parseClassType = clazz.parseClassType = function (componentType) { + var ret = {main: '', sub: ''}; + if (componentType) { + componentType = componentType.split(TYPE_DELIMITER); + ret.main = componentType[0] || ''; + ret.sub = componentType[1] || ''; + } + return ret; + }; + /** + * @public + */ + clazz.enableClassExtend = function (RootClass) { + + RootClass.$constructor = RootClass; + RootClass.extend = function (proto) { + var superClass = this; + var ExtendedClass = function () { + if (!proto.$constructor) { + superClass.apply(this, arguments); + } + else { + proto.$constructor.apply(this, arguments); + } + }; + + zrUtil.extend(ExtendedClass.prototype, proto); + + ExtendedClass.extend = this.extend; + ExtendedClass.superCall = superCall; + ExtendedClass.superApply = superApply; + zrUtil.inherits(ExtendedClass, this); + ExtendedClass.superClass = superClass; + + return ExtendedClass; + }; + }; + + // superCall should have class info, which can not be fetch from 'this'. + // Consider this case: + // class A has method f, + // class B inherits class A, overrides method f, f call superApply('f'), + // class C inherits class B, do not overrides method f, + // then when method of class C is called, dead loop occured. + function superCall(context, methodName) { + var args = zrUtil.slice(arguments, 2); + return this.superClass.prototype[methodName].apply(context, args); + } + + function superApply(context, methodName, args) { + return this.superClass.prototype[methodName].apply(context, args); + } + + /** + * @param {Object} entity + * @param {Object} options + * @param {boolean} [options.registerWhenExtend] + * @public + */ + clazz.enableClassManagement = function (entity, options) { + options = options || {}; + + /** + * Component model classes + * key: componentType, + * value: + * componentClass, when componentType is 'xxx' + * or Object., when componentType is 'xxx.yy' + * @type {Object} + */ + var storage = {}; + + entity.registerClass = function (Clazz, componentType) { + if (componentType) { + componentType = parseClassType(componentType); + + if (!componentType.sub) { + if (true) { + if (storage[componentType.main]) { + console.warn(componentType.main + ' exists.'); + } + } + storage[componentType.main] = Clazz; + } + else if (componentType.sub !== IS_CONTAINER) { + var container = makeContainer(componentType); + container[componentType.sub] = Clazz; + } + } + return Clazz; + }; + + entity.getClass = function (componentTypeMain, subType, throwWhenNotFound) { + var Clazz = storage[componentTypeMain]; + + if (Clazz && Clazz[IS_CONTAINER]) { + Clazz = subType ? Clazz[subType] : null; + } + + if (throwWhenNotFound && !Clazz) { + throw new Error( + 'Component ' + componentTypeMain + '.' + (subType || '') + ' not exists. Load it first.' + ); + } + + return Clazz; + }; + + entity.getClassesByMainType = function (componentType) { + componentType = parseClassType(componentType); + + var result = []; + var obj = storage[componentType.main]; + + if (obj && obj[IS_CONTAINER]) { + zrUtil.each(obj, function (o, type) { + type !== IS_CONTAINER && result.push(o); + }); + } + else { + result.push(obj); + } + + return result; + }; + + entity.hasClass = function (componentType) { + // Just consider componentType.main. + componentType = parseClassType(componentType); + return !!storage[componentType.main]; + }; + + /** + * @return {Array.} Like ['aa', 'bb'], but can not be ['aa.xx'] + */ + entity.getAllClassMainTypes = function () { + var types = []; + zrUtil.each(storage, function (obj, type) { + types.push(type); + }); + return types; + }; + + /** + * If a main type is container and has sub types + * @param {string} mainType + * @return {boolean} + */ + entity.hasSubTypes = function (componentType) { + componentType = parseClassType(componentType); + var obj = storage[componentType.main]; + return obj && obj[IS_CONTAINER]; + }; + + entity.parseClassType = parseClassType; + + function makeContainer(componentType) { + var container = storage[componentType.main]; + if (!container || !container[IS_CONTAINER]) { + container = storage[componentType.main] = {}; + container[IS_CONTAINER] = true; + } + return container; + } + + if (options.registerWhenExtend) { + var originalExtend = entity.extend; + if (originalExtend) { + entity.extend = function (proto) { + var ExtendedClass = originalExtend.call(this, proto); + return entity.registerClass(ExtendedClass, proto.type); + }; + } + } + + return entity; + }; + + /** + * @param {string|Array.} properties + */ + clazz.setReadOnly = function (obj, properties) { + // FIXME It seems broken in IE8 simulation of IE11 + // if (!zrUtil.isArray(properties)) { + // properties = properties != null ? [properties] : []; + // } + // zrUtil.each(properties, function (prop) { + // var value = obj[prop]; + + // Object.defineProperty + // && Object.defineProperty(obj, prop, { + // value: value, writable: false + // }); + // zrUtil.isArray(obj[prop]) + // && Object.freeze + // && Object.freeze(obj[prop]); + // }); + }; + + module.exports = clazz; + + +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + + var getLineStyle = __webpack_require__(15)( + [ + ['lineWidth', 'width'], + ['stroke', 'color'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getLineStyle: function (excludes) { + var style = getLineStyle.call(this, excludes); + var lineDash = this.getLineDash(); + lineDash && (style.lineDash = lineDash); + return style; + }, + + getLineDash: function () { + var lineType = this.get('type'); + return (lineType === 'solid' || lineType == null) ? null + : (lineType === 'dashed' ? [5, 5] : [2, 2]); + } + }; + + +/***/ }, +/* 15 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Parse shadow style + // TODO Only shallow path support + + var zrUtil = __webpack_require__(4); + + module.exports = function (properties) { + // Normalize + for (var i = 0; i < properties.length; i++) { + if (!properties[i][1]) { + properties[i][1] = properties[i][0]; + } + } + return function (excludes) { + var style = {}; + for (var i = 0; i < properties.length; i++) { + var propName = properties[i][1]; + if (excludes && zrUtil.indexOf(excludes, propName) >= 0) { + continue; + } + var val = this.getShallow(propName); + if (val != null) { + style[properties[i][0]] = val; + } + } + return style; + }; + }; + + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + + module.exports = { + getAreaStyle: __webpack_require__(15)( + [ + ['fill', 'color'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['opacity'], + ['shadowColor'] + ] + ) + }; + + +/***/ }, +/* 17 */ +/***/ function(module, exports, __webpack_require__) { + + + + var textContain = __webpack_require__(8); + + function getShallow(model, path) { + return model && model.getShallow(path); + } + + module.exports = { + /** + * Get color property or get color from option.textStyle.color + * @return {string} + */ + getTextColor: function () { + var ecModel = this.ecModel; + return this.getShallow('color') + || (ecModel && ecModel.get('textStyle.color')); + }, + + /** + * Create font string from fontStyle, fontWeight, fontSize, fontFamily + * @return {string} + */ + getFont: function () { + var ecModel = this.ecModel; + var gTextStyleModel = ecModel && ecModel.getModel('textStyle'); + return [ + // FIXME in node-canvas fontWeight is before fontStyle + this.getShallow('fontStyle') || getShallow(gTextStyleModel, 'fontStyle'), + this.getShallow('fontWeight') || getShallow(gTextStyleModel, 'fontWeight'), + (this.getShallow('fontSize') || getShallow(gTextStyleModel, 'fontSize') || 12) + 'px', + this.getShallow('fontFamily') || getShallow(gTextStyleModel, 'fontFamily') || 'sans-serif' + ].join(' '); + }, + + getTextRect: function (text) { + var textStyle = this.get('textStyle') || {}; + return textContain.getBoundingRect( + text, + this.getFont(), + textStyle.align, + textStyle.baseline + ); + }, + + truncateText: function (text, containerWidth, ellipsis, options) { + return textContain.truncateText( + text, containerWidth, this.getFont(), ellipsis, options + ); + } + }; + + +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { + + + var getItemStyle = __webpack_require__(15)( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getItemStyle: function (excludes) { + var style = getItemStyle.call(this, excludes); + var lineDash = this.getBorderLineDash(); + lineDash && (style.lineDash = lineDash); + return style; + }, + + getBorderLineDash: function () { + var lineType = this.get('borderType'); + return (lineType === 'solid' || lineType == null) ? null + : (lineType === 'dashed' ? [5, 5] : [1, 1]); + } + }; + + +/***/ }, +/* 19 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Component model + * + * @module echarts/model/Component + */ + + + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + var arrayPush = Array.prototype.push; + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + var layout = __webpack_require__(21); + + /** + * @alias module:echarts/model/Component + * @constructor + * @param {Object} option + * @param {module:echarts/model/Model} parentModel + * @param {module:echarts/model/Model} ecModel + */ + var ComponentModel = Model.extend({ + + type: 'component', + + /** + * @readOnly + * @type {string} + */ + id: '', + + /** + * @readOnly + */ + name: '', + + /** + * @readOnly + * @type {string} + */ + mainType: '', + + /** + * @readOnly + * @type {string} + */ + subType: '', + + /** + * @readOnly + * @type {number} + */ + componentIndex: 0, + + /** + * @type {Object} + * @protected + */ + defaultOption: null, + + /** + * @type {module:echarts/model/Global} + * @readOnly + */ + ecModel: null, + + /** + * key: componentType + * value: Component model list, can not be null. + * @type {Object.>} + * @readOnly + */ + dependentModels: [], + + /** + * @type {string} + * @readOnly + */ + uid: null, + + /** + * Support merge layout params. + * Only support 'box' now (left/right/top/bottom/width/height). + * @type {string|Object} Object can be {ignoreSize: true} + * @readOnly + */ + layoutMode: null, + + $constructor: function (option, parentModel, ecModel, extraOpt) { + Model.call(this, option, parentModel, ecModel, extraOpt); + + // Set dependentModels, componentIndex, name, id, mainType, subType. + zrUtil.extend(this, extraOpt); + + this.uid = componentUtil.getUID('componentModel'); + }, + + + init: function (option, parentModel, ecModel, extraOpt) { + this.mergeDefaultAndTheme(option, ecModel); + }, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? layout.getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + zrUtil.merge(option, themeModel.get(this.mainType)); + zrUtil.merge(option, this.getDefaultOption()); + + if (layoutMode) { + layout.mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + mergeOption: function (option) { + zrUtil.merge(this.option, option, true); + + var layoutMode = this.layoutMode; + if (layoutMode) { + layout.mergeLayoutParam(this.option, option, layoutMode); + } + }, + + // Hooker after init or mergeOption + optionUpdated: function (newCptOption, isInit) {}, + + getDefaultOption: function () { + if (!this.hasOwnProperty('__defaultOption')) { + var optList = []; + var Class = this.constructor; + while (Class) { + var opt = Class.prototype.defaultOption; + opt && optList.push(opt); + Class = Class.superClass; + } + + var defaultOption = {}; + for (var i = optList.length - 1; i >= 0; i--) { + defaultOption = zrUtil.merge(defaultOption, optList[i], true); + } + this.__defaultOption = defaultOption; + } + return this.__defaultOption; + } + + }); + + // Reset ComponentModel.extend, add preConstruct. + // clazzUtil.enableClassExtend( + // ComponentModel, + // function (option, parentModel, ecModel, extraOpt) { + // // Set dependentModels, componentIndex, name, id, mainType, subType. + // zrUtil.extend(this, extraOpt); + + // this.uid = componentUtil.getUID('componentModel'); + + // // this.setReadOnly([ + // // 'type', 'id', 'uid', 'name', 'mainType', 'subType', + // // 'dependentModels', 'componentIndex' + // // ]); + // } + // ); + + // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement( + ComponentModel, {registerWhenExtend: true} + ); + componentUtil.enableSubTypeDefaulter(ComponentModel); + + // Add capability of ComponentModel.topologicalTravel. + componentUtil.enableTopologicalTravel(ComponentModel, getDependencies); + + function getDependencies(componentType) { + var deps = []; + zrUtil.each(ComponentModel.getClassesByMainType(componentType), function (Clazz) { + arrayPush.apply(deps, Clazz.prototype.dependencies || []); + }); + // Ensure main type + return zrUtil.map(deps, function (type) { + return clazzUtil.parseClassType(type).main; + }); + } + + zrUtil.mixin(ComponentModel, __webpack_require__(22)); + + module.exports = ComponentModel; + + +/***/ }, +/* 20 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var clazz = __webpack_require__(13); + + var parseClassType = clazz.parseClassType; + + var base = 0; + + var componentUtil = {}; + + var DELIMITER = '_'; + + /** + * @public + * @param {string} type + * @return {string} + */ + componentUtil.getUID = function (type) { + // Considering the case of crossing js context, + // use Math.random to make id as unique as possible. + return [(type || ''), base++, Math.random()].join(DELIMITER); + }; + + /** + * @inner + */ + componentUtil.enableSubTypeDefaulter = function (entity) { + + var subTypeDefaulters = {}; + + entity.registerSubTypeDefaulter = function (componentType, defaulter) { + componentType = parseClassType(componentType); + subTypeDefaulters[componentType.main] = defaulter; + }; + + entity.determineSubType = function (componentType, option) { + var type = option.type; + if (!type) { + var componentTypeMain = parseClassType(componentType).main; + if (entity.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) { + type = subTypeDefaulters[componentTypeMain](option); + } + } + return type; + }; + + return entity; + }; + + /** + * Topological travel on Activity Network (Activity On Vertices). + * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. + * + * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. + * + * If there is circle dependencey, Error will be thrown. + * + */ + componentUtil.enableTopologicalTravel = function (entity, dependencyGetter) { + + /** + * @public + * @param {Array.} targetNameList Target Component type list. + * Can be ['aa', 'bb', 'aa.xx'] + * @param {Array.} fullNameList By which we can build dependency graph. + * @param {Function} callback Params: componentType, dependencies. + * @param {Object} context Scope of callback. + */ + entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) { + if (!targetNameList.length) { + return; + } + + var result = makeDepndencyGraph(fullNameList); + var graph = result.graph; + var stack = result.noEntryList; + + var targetNameSet = {}; + zrUtil.each(targetNameList, function (name) { + targetNameSet[name] = true; + }); + + while (stack.length) { + var currComponentType = stack.pop(); + var currVertex = graph[currComponentType]; + var isInTargetNameSet = !!targetNameSet[currComponentType]; + if (isInTargetNameSet) { + callback.call(context, currComponentType, currVertex.originalDeps.slice()); + delete targetNameSet[currComponentType]; + } + zrUtil.each( + currVertex.successor, + isInTargetNameSet ? removeEdgeAndAdd : removeEdge + ); + } + + zrUtil.each(targetNameSet, function () { + throw new Error('Circle dependency may exists'); + }); + + function removeEdge(succComponentType) { + graph[succComponentType].entryCount--; + if (graph[succComponentType].entryCount === 0) { + stack.push(succComponentType); + } + } + + // Consider this case: legend depends on series, and we call + // chart.setOption({series: [...]}), where only series is in option. + // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will + // not be called, but only sereis.mergeOption is called. Thus legend + // have no chance to update its local record about series (like which + // name of series is available in legend). + function removeEdgeAndAdd(succComponentType) { + targetNameSet[succComponentType] = true; + removeEdge(succComponentType); + } + }; + + /** + * DepndencyGraph: {Object} + * key: conponentType, + * value: { + * successor: [conponentTypes...], + * originalDeps: [conponentTypes...], + * entryCount: {number} + * } + */ + function makeDepndencyGraph(fullNameList) { + var graph = {}; + var noEntryList = []; + + zrUtil.each(fullNameList, function (name) { + + var thisItem = createDependencyGraphItem(graph, name); + var originalDeps = thisItem.originalDeps = dependencyGetter(name); + + var availableDeps = getAvailableDependencies(originalDeps, fullNameList); + thisItem.entryCount = availableDeps.length; + if (thisItem.entryCount === 0) { + noEntryList.push(name); + } + + zrUtil.each(availableDeps, function (dependentName) { + if (zrUtil.indexOf(thisItem.predecessor, dependentName) < 0) { + thisItem.predecessor.push(dependentName); + } + var thatItem = createDependencyGraphItem(graph, dependentName); + if (zrUtil.indexOf(thatItem.successor, dependentName) < 0) { + thatItem.successor.push(name); + } + }); + }); + + return {graph: graph, noEntryList: noEntryList}; + } + + function createDependencyGraphItem(graph, name) { + if (!graph[name]) { + graph[name] = {predecessor: [], successor: []}; + } + return graph[name]; + } + + function getAvailableDependencies(originalDeps, fullNameList) { + var availableDeps = []; + zrUtil.each(originalDeps, function (dep) { + zrUtil.indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); + }); + return availableDeps; + } + }; + + module.exports = componentUtil; + + +/***/ }, +/* 21 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Layout helpers for each component positioning + + + var zrUtil = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var parsePercent = numberUtil.parsePercent; + var each = zrUtil.each; + + var layout = {}; + + var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height']; + + function boxLayout(orient, group, gap, maxWidth, maxHeight) { + var x = 0; + var y = 0; + if (maxWidth == null) { + maxWidth = Infinity; + } + if (maxHeight == null) { + maxHeight = Infinity; + } + var currentLineMaxSize = 0; + group.eachChild(function (child, idx) { + var position = child.position; + var rect = child.getBoundingRect(); + var nextChild = group.childAt(idx + 1); + var nextChildRect = nextChild && nextChild.getBoundingRect(); + var nextX; + var nextY; + if (orient === 'horizontal') { + var moveX = rect.width + (nextChildRect ? (-nextChildRect.x + rect.x) : 0); + nextX = x + moveX; + // Wrap when width exceeds maxWidth or meet a `newline` group + if (nextX > maxWidth || child.newline) { + x = 0; + nextX = moveX; + y += currentLineMaxSize + gap; + currentLineMaxSize = rect.height; + } + else { + currentLineMaxSize = Math.max(currentLineMaxSize, rect.height); + } + } + else { + var moveY = rect.height + (nextChildRect ? (-nextChildRect.y + rect.y) : 0); + nextY = y + moveY; + // Wrap when width exceeds maxHeight or meet a `newline` group + if (nextY > maxHeight || child.newline) { + x += currentLineMaxSize + gap; + y = 0; + nextY = moveY; + currentLineMaxSize = rect.width; + } + else { + currentLineMaxSize = Math.max(currentLineMaxSize, rect.width); + } + } + + if (child.newline) { + return; + } + + position[0] = x; + position[1] = y; + + orient === 'horizontal' + ? (x = nextX + gap) + : (y = nextY + gap); + }); + } + + /** + * VBox or HBox layouting + * @param {string} orient + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.box = boxLayout; + + /** + * VBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.vbox = zrUtil.curry(boxLayout, 'vertical'); + + /** + * HBox layouting + * @param {module:zrender/container/Group} group + * @param {number} gap + * @param {number} [width=Infinity] + * @param {number} [height=Infinity] + */ + layout.hbox = zrUtil.curry(boxLayout, 'horizontal'); + + /** + * If x or x2 is not specified or 'center' 'left' 'right', + * the width would be as long as possible. + * If y or y2 is not specified or 'middle' 'top' 'bottom', + * the height would be as long as possible. + * + * @param {Object} positionInfo + * @param {number|string} [positionInfo.x] + * @param {number|string} [positionInfo.y] + * @param {number|string} [positionInfo.x2] + * @param {number|string} [positionInfo.y2] + * @param {Object} containerRect + * @param {string|number} margin + * @return {Object} {width, height} + */ + layout.getAvailableSize = function (positionInfo, containerRect, margin) { + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var x = parsePercent(positionInfo.x, containerWidth); + var y = parsePercent(positionInfo.y, containerHeight); + var x2 = parsePercent(positionInfo.x2, containerWidth); + var y2 = parsePercent(positionInfo.y2, containerHeight); + + (isNaN(x) || isNaN(parseFloat(positionInfo.x))) && (x = 0); + (isNaN(x2) || isNaN(parseFloat(positionInfo.x2))) && (x2 = containerWidth); + (isNaN(y) || isNaN(parseFloat(positionInfo.y))) && (y = 0); + (isNaN(y2) || isNaN(parseFloat(positionInfo.y2))) && (y2 = containerHeight); + + margin = formatUtil.normalizeCssArray(margin || 0); + + return { + width: Math.max(x2 - x - margin[1] - margin[3], 0), + height: Math.max(y2 - y - margin[0] - margin[2], 0) + }; + }; + + /** + * Parse position info. + * + * @param {Object} positionInfo + * @param {number|string} [positionInfo.left] + * @param {number|string} [positionInfo.top] + * @param {number|string} [positionInfo.right] + * @param {number|string} [positionInfo.bottom] + * @param {number|string} [positionInfo.width] + * @param {number|string} [positionInfo.height] + * @param {number|string} [positionInfo.aspect] Aspect is width / height + * @param {Object} containerRect + * @param {string|number} [margin] + * + * @return {module:zrender/core/BoundingRect} + */ + layout.getLayoutRect = function ( + positionInfo, containerRect, margin + ) { + margin = formatUtil.normalizeCssArray(margin || 0); + + var containerWidth = containerRect.width; + var containerHeight = containerRect.height; + + var left = parsePercent(positionInfo.left, containerWidth); + var top = parsePercent(positionInfo.top, containerHeight); + var right = parsePercent(positionInfo.right, containerWidth); + var bottom = parsePercent(positionInfo.bottom, containerHeight); + var width = parsePercent(positionInfo.width, containerWidth); + var height = parsePercent(positionInfo.height, containerHeight); + + var verticalMargin = margin[2] + margin[0]; + var horizontalMargin = margin[1] + margin[3]; + var aspect = positionInfo.aspect; + + // If width is not specified, calculate width from left and right + if (isNaN(width)) { + width = containerWidth - right - horizontalMargin - left; + } + if (isNaN(height)) { + height = containerHeight - bottom - verticalMargin - top; + } + + // If width and height are not given + // 1. Graph should not exceeds the container + // 2. Aspect must be keeped + // 3. Graph should take the space as more as possible + if (isNaN(width) && isNaN(height)) { + if (aspect > containerWidth / containerHeight) { + width = containerWidth * 0.8; + } + else { + height = containerHeight * 0.8; + } + } + + if (aspect != null) { + // Calculate width or height with given aspect + if (isNaN(width)) { + width = aspect * height; + } + if (isNaN(height)) { + height = width / aspect; + } + } + + // If left is not specified, calculate left from right and width + if (isNaN(left)) { + left = containerWidth - right - width - horizontalMargin; + } + if (isNaN(top)) { + top = containerHeight - bottom - height - verticalMargin; + } + + // Align left and top + switch (positionInfo.left || positionInfo.right) { + case 'center': + left = containerWidth / 2 - width / 2 - margin[3]; + break; + case 'right': + left = containerWidth - width - horizontalMargin; + break; + } + switch (positionInfo.top || positionInfo.bottom) { + case 'middle': + case 'center': + top = containerHeight / 2 - height / 2 - margin[0]; + break; + case 'bottom': + top = containerHeight - height - verticalMargin; + break; + } + // If something is wrong and left, top, width, height are calculated as NaN + left = left || 0; + top = top || 0; + if (isNaN(width)) { + // Width may be NaN if only one value is given except width + width = containerWidth - left - (right || 0); + } + if (isNaN(height)) { + // Height may be NaN if only one value is given except height + height = containerHeight - top - (bottom || 0); + } + + var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); + rect.margin = margin; + return rect; + }; + + /** + * Position group of component in viewport + * Group position is specified by either + * {left, top}, {right, bottom} + * If all properties exists, right and bottom will be igonred. + * + * @param {module:zrender/container/Group} group + * @param {Object} positionInfo + * @param {number|string} [positionInfo.left] + * @param {number|string} [positionInfo.top] + * @param {number|string} [positionInfo.right] + * @param {number|string} [positionInfo.bottom] + * @param {Object} containerRect + * @param {string|number} margin + */ + layout.positionGroup = function ( + group, positionInfo, containerRect, margin + ) { + var groupRect = group.getBoundingRect(); + + positionInfo = zrUtil.extend(zrUtil.clone(positionInfo), { + width: groupRect.width, + height: groupRect.height + }); + + positionInfo = layout.getLayoutRect( + positionInfo, containerRect, margin + ); + + group.attr('position', [ + positionInfo.x - groupRect.x, + positionInfo.y - groupRect.y + ]); + }; + + /** + * Consider Case: + * When defulat option has {left: 0, width: 100}, and we set {right: 0} + * through setOption or media query, using normal zrUtil.merge will cause + * {right: 0} does not take effect. + * + * @example + * ComponentModel.extend({ + * init: function () { + * ... + * var inputPositionParams = layout.getLayoutParams(option); + * this.mergeOption(inputPositionParams); + * }, + * mergeOption: function (newOption) { + * newOption && zrUtil.merge(thisOption, newOption, true); + * layout.mergeLayoutParam(thisOption, newOption); + * } + * }); + * + * @param {Object} targetOption + * @param {Object} newOption + * @param {Object|string} [opt] + * @param {boolean} [opt.ignoreSize=false] Some component must has width and height. + */ + layout.mergeLayoutParam = function (targetOption, newOption, opt) { + !zrUtil.isObject(opt) && (opt = {}); + var hNames = ['width', 'left', 'right']; // Order by priority. + var vNames = ['height', 'top', 'bottom']; // Order by priority. + var hResult = merge(hNames); + var vResult = merge(vNames); + + copy(hNames, targetOption, hResult); + copy(vNames, targetOption, vResult); + + function merge(names) { + var newParams = {}; + var newValueCount = 0; + var merged = {}; + var mergedValueCount = 0; + var enoughParamNumber = opt.ignoreSize ? 1 : 2; + + each(names, function (name) { + merged[name] = targetOption[name]; + }); + each(names, function (name) { + // Consider case: newOption.width is null, which is + // set by user for removing width setting. + hasProp(newOption, name) && (newParams[name] = merged[name] = newOption[name]); + hasValue(newParams, name) && newValueCount++; + hasValue(merged, name) && mergedValueCount++; + }); + + // Case: newOption: {width: ..., right: ...}, + // or targetOption: {right: ...} and newOption: {width: ...}, + // There is no conflict when merged only has params count + // little than enoughParamNumber. + if (mergedValueCount === enoughParamNumber || !newValueCount) { + return merged; + } + // Case: newOption: {width: ..., right: ...}, + // Than we can make sure user only want those two, and ignore + // all origin params in targetOption. + else if (newValueCount >= enoughParamNumber) { + return newParams; + } + else { + // Chose another param from targetOption by priority. + // When 'ignoreSize', enoughParamNumber is 1 and those will not happen. + for (var i = 0; i < names.length; i++) { + var name = names[i]; + if (!hasProp(newParams, name) && hasProp(targetOption, name)) { + newParams[name] = targetOption[name]; + break; + } + } + return newParams; + } + } + + function hasProp(obj, name) { + return obj.hasOwnProperty(name); + } + + function hasValue(obj, name) { + return obj[name] != null && obj[name] !== 'auto'; + } + + function copy(names, target, source) { + each(names, function (name) { + target[name] = source[name]; + }); + } + }; + + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ + layout.getLayoutParams = function (source) { + return layout.copyLayoutParams({}, source); + }; + + /** + * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. + * @param {Object} source + * @return {Object} Result contains those props. + */ + layout.copyLayoutParams = function (target, source) { + source && target && each(LOCATION_PARAMS, function (name) { + source.hasOwnProperty(name) && (target[name] = source[name]); + }); + return target; + }; + + module.exports = layout; + + +/***/ }, +/* 22 */ +/***/ function(module, exports) { + + + + module.exports = { + getBoxLayoutParams: function () { + return { + left: this.get('left'), + top: this.get('top'), + right: this.get('right'), + bottom: this.get('bottom'), + width: this.get('width'), + height: this.get('height') + }; + } + }; + + +/***/ }, +/* 23 */ +/***/ function(module, exports) { + + + var platform = ''; + // Navigator not exists in node + if (typeof navigator !== 'undefined') { + platform = navigator.platform || ''; + } + module.exports = { + // 全图默认背景 + // backgroundColor: 'rgba(0,0,0,0)', + + // https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization + // color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'], + // 浅色 + // color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'], + // color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'], + // 深色 + color: ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'], + + // 默认需要 Grid 配置项 + // grid: {}, + // 主题,主题 + textStyle: { + // color: '#000', + // decoration: 'none', + // PENDING + fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif', + // fontFamily: 'Arial, Verdana, sans-serif', + fontSize: 12, + fontStyle: 'normal', + fontWeight: 'normal' + }, + + // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/ + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + // Default is source-over + blendMode: null, + + animation: true, + animationDuration: 1000, + animationDurationUpdate: 300, + animationEasing: 'exponentialOut', + animationEasingUpdate: 'cubicOut', + + animationThreshold: 2000, + // Configuration for progressive/incremental rendering + progressiveThreshold: 3000, + progressive: 400, + + // Threshold of if use single hover layer to optimize. + hoverLayerThreshold: 3000 + }; + + +/***/ }, +/* 24 */ +/***/ function(module, exports) { + + + module.exports = { + clearColorPalette: function () { + this._colorIdx = 0; + this._colorNameMap = {}; + }, + + getColorFromPalette: function (name, scope) { + scope = scope || this; + var colorIdx = scope._colorIdx || 0; + var colorNameMap = scope._colorNameMap || (scope._colorNameMap = {}); + if (colorNameMap[name]) { + return colorNameMap[name]; + } + var colorPalette = this.get('color', true) || []; + if (!colorPalette.length) { + return; + } + + var color = colorPalette[colorIdx]; + if (name) { + colorNameMap[name] = color; + } + scope._colorIdx = (colorIdx + 1) % colorPalette.length; + + return color; + } + }; + + +/***/ }, +/* 25 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var echartsAPIList = [ + 'getDom', 'getZr', 'getWidth', 'getHeight', 'dispatchAction', 'isDisposed', + 'on', 'off', 'getDataURL', 'getConnectedDataURL', 'getModel', 'getOption' + ]; + + function ExtensionAPI(chartInstance) { + zrUtil.each(echartsAPIList, function (name) { + this[name] = zrUtil.bind(chartInstance[name], chartInstance); + }, this); + } + + module.exports = ExtensionAPI; + + +/***/ }, +/* 26 */ +/***/ function(module, exports) { + + 'use strict'; + + + var coordinateSystemCreators = {}; + + function CoordinateSystemManager() { + + this._coordinateSystems = []; + } + + CoordinateSystemManager.prototype = { + + constructor: CoordinateSystemManager, + + create: function (ecModel, api) { + var coordinateSystems = []; + for (var type in coordinateSystemCreators) { + var list = coordinateSystemCreators[type].create(ecModel, api); + list && (coordinateSystems = coordinateSystems.concat(list)); + } + + this._coordinateSystems = coordinateSystems; + }, + + update: function (ecModel, api) { + var coordinateSystems = this._coordinateSystems; + for (var i = 0; i < coordinateSystems.length; i++) { + // FIXME MUST have + coordinateSystems[i].update && coordinateSystems[i].update(ecModel, api); + } + } + }; + + CoordinateSystemManager.register = function (type, coordinateSystemCreator) { + coordinateSystemCreators[type] = coordinateSystemCreator; + }; + + CoordinateSystemManager.get = function (type) { + return coordinateSystemCreators[type]; + }; + + module.exports = CoordinateSystemManager; + + +/***/ }, +/* 27 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * ECharts option manager + * + * @module {echarts/model/OptionManager} + */ + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var each = zrUtil.each; + var clone = zrUtil.clone; + var map = zrUtil.map; + var merge = zrUtil.merge; + + var QUERY_REG = /^(min|max)?(.+)$/; + + /** + * TERM EXPLANATIONS: + * + * [option]: + * + * An object that contains definitions of components. For example: + * var option = { + * title: {...}, + * legend: {...}, + * visualMap: {...}, + * series: [ + * {data: [...]}, + * {data: [...]}, + * ... + * ] + * }; + * + * [rawOption]: + * + * An object input to echarts.setOption. 'rawOption' may be an + * 'option', or may be an object contains multi-options. For example: + * var option = { + * baseOption: { + * title: {...}, + * legend: {...}, + * series: [ + * {data: [...]}, + * {data: [...]}, + * ... + * ] + * }, + * timeline: {...}, + * options: [ + * {title: {...}, series: {data: [...]}}, + * {title: {...}, series: {data: [...]}}, + * ... + * ], + * media: [ + * { + * query: {maxWidth: 320}, + * option: {series: {x: 20}, visualMap: {show: false}} + * }, + * { + * query: {minWidth: 320, maxWidth: 720}, + * option: {series: {x: 500}, visualMap: {show: true}} + * }, + * { + * option: {series: {x: 1200}, visualMap: {show: true}} + * } + * ] + * }; + * + * @alias module:echarts/model/OptionManager + * @param {module:echarts/ExtensionAPI} api + */ + function OptionManager(api) { + + /** + * @private + * @type {module:echarts/ExtensionAPI} + */ + this._api = api; + + /** + * @private + * @type {Array.} + */ + this._timelineOptions = []; + + /** + * @private + * @type {Array.} + */ + this._mediaList = []; + + /** + * @private + * @type {Object} + */ + this._mediaDefault; + + /** + * -1, means default. + * empty means no media. + * @private + * @type {Array.} + */ + this._currentMediaIndices = []; + + /** + * @private + * @type {Object} + */ + this._optionBackup; + + /** + * @private + * @type {Object} + */ + this._newBaseOption; + } + + // timeline.notMerge is not supported in ec3. Firstly there is rearly + // case that notMerge is needed. Secondly supporting 'notMerge' requires + // rawOption cloned and backuped when timeline changed, which does no + // good to performance. What's more, that both timeline and setOption + // method supply 'notMerge' brings complex and some problems. + // Consider this case: + // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); + // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); + + OptionManager.prototype = { + + constructor: OptionManager, + + /** + * @public + * @param {Object} rawOption Raw option. + * @param {module:echarts/model/Global} ecModel + * @param {Array.} optionPreprocessorFuncs + * @return {Object} Init option + */ + setOption: function (rawOption, optionPreprocessorFuncs) { + rawOption = clone(rawOption, true); + + // FIXME + // 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。 + + var oldOptionBackup = this._optionBackup; + var newParsedOption = parseRawOption.call( + this, rawOption, optionPreprocessorFuncs, !oldOptionBackup + ); + this._newBaseOption = newParsedOption.baseOption; + + // For setOption at second time (using merge mode); + if (oldOptionBackup) { + // Only baseOption can be merged. + mergeOption(oldOptionBackup.baseOption, newParsedOption.baseOption); + + // For simplicity, timeline options and media options do not support merge, + // that is, if you `setOption` twice and both has timeline options, the latter + // timeline opitons will not be merged to the formers, but just substitude them. + if (newParsedOption.timelineOptions.length) { + oldOptionBackup.timelineOptions = newParsedOption.timelineOptions; + } + if (newParsedOption.mediaList.length) { + oldOptionBackup.mediaList = newParsedOption.mediaList; + } + if (newParsedOption.mediaDefault) { + oldOptionBackup.mediaDefault = newParsedOption.mediaDefault; + } + } + else { + this._optionBackup = newParsedOption; + } + }, + + /** + * @param {boolean} isRecreate + * @return {Object} + */ + mountOption: function (isRecreate) { + var optionBackup = this._optionBackup; + + // TODO + // 如果没有reset功能则不clone。 + + this._timelineOptions = map(optionBackup.timelineOptions, clone); + this._mediaList = map(optionBackup.mediaList, clone); + this._mediaDefault = clone(optionBackup.mediaDefault); + this._currentMediaIndices = []; + + return clone(isRecreate + // this._optionBackup.baseOption, which is created at the first `setOption` + // called, and is merged into every new option by inner method `mergeOption` + // each time `setOption` called, can be only used in `isRecreate`, because + // its reliability is under suspicion. In other cases option merge is + // proformed by `model.mergeOption`. + ? optionBackup.baseOption : this._newBaseOption + ); + }, + + /** + * @param {module:echarts/model/Global} ecModel + * @return {Object} + */ + getTimelineOption: function (ecModel) { + var option; + var timelineOptions = this._timelineOptions; + + if (timelineOptions.length) { + // getTimelineOption can only be called after ecModel inited, + // so we can get currentIndex from timelineModel. + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel) { + option = clone( + timelineOptions[timelineModel.getCurrentIndex()], + true + ); + } + } + + return option; + }, + + /** + * @param {module:echarts/model/Global} ecModel + * @return {Array.} + */ + getMediaOption: function (ecModel) { + var ecWidth = this._api.getWidth(); + var ecHeight = this._api.getHeight(); + var mediaList = this._mediaList; + var mediaDefault = this._mediaDefault; + var indices = []; + var result = []; + + // No media defined. + if (!mediaList.length && !mediaDefault) { + return result; + } + + // Multi media may be applied, the latter defined media has higher priority. + for (var i = 0, len = mediaList.length; i < len; i++) { + if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { + indices.push(i); + } + } + + // FIXME + // 是否mediaDefault应该强制用户设置,否则可能修改不能回归。 + if (!indices.length && mediaDefault) { + indices = [-1]; + } + + if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { + result = map(indices, function (index) { + return clone( + index === -1 ? mediaDefault.option : mediaList[index].option + ); + }); + } + // Otherwise return nothing. + + this._currentMediaIndices = indices; + + return result; + } + }; + + function parseRawOption(rawOption, optionPreprocessorFuncs, isNew) { + var timelineOptions = []; + var mediaList = []; + var mediaDefault; + var baseOption; + + // Compatible with ec2. + var timelineOpt = rawOption.timeline; + + if (rawOption.baseOption) { + baseOption = rawOption.baseOption; + } + + // For timeline + if (timelineOpt || rawOption.options) { + baseOption = baseOption || {}; + timelineOptions = (rawOption.options || []).slice(); + } + + // For media query + if (rawOption.media) { + baseOption = baseOption || {}; + var media = rawOption.media; + each(media, function (singleMedia) { + if (singleMedia && singleMedia.option) { + if (singleMedia.query) { + mediaList.push(singleMedia); + } + else if (!mediaDefault) { + // Use the first media default. + mediaDefault = singleMedia; + } + } + }); + } + + // For normal option + if (!baseOption) { + baseOption = rawOption; + } + + // Set timelineOpt to baseOption in ec3, + // which is convenient for merge option. + if (!baseOption.timeline) { + baseOption.timeline = timelineOpt; + } + + // Preprocess. + each([baseOption].concat(timelineOptions) + .concat(zrUtil.map(mediaList, function (media) { + return media.option; + })), + function (option) { + each(optionPreprocessorFuncs, function (preProcess) { + preProcess(option, isNew); + }); + } + ); + + return { + baseOption: baseOption, + timelineOptions: timelineOptions, + mediaDefault: mediaDefault, + mediaList: mediaList + }; + } + + /** + * @see + * Support: width, height, aspectRatio + * Can use max or min as prefix. + */ + function applyMediaQuery(query, ecWidth, ecHeight) { + var realMap = { + width: ecWidth, + height: ecHeight, + aspectratio: ecWidth / ecHeight // lowser case for convenientce. + }; + + var applicatable = true; + + zrUtil.each(query, function (value, attr) { + var matched = attr.match(QUERY_REG); + + if (!matched || !matched[1] || !matched[2]) { + return; + } + + var operator = matched[1]; + var realAttr = matched[2].toLowerCase(); + + if (!compare(realMap[realAttr], value, operator)) { + applicatable = false; + } + }); + + return applicatable; + } + + function compare(real, expect, operator) { + if (operator === 'min') { + return real >= expect; + } + else if (operator === 'max') { + return real <= expect; + } + else { // Equals + return real === expect; + } + } + + function indicesEquals(indices1, indices2) { + // indices is always order by asc and has only finite number. + return indices1.join(',') === indices2.join(','); + } + + /** + * Consider case: + * `chart.setOption(opt1);` + * Then user do some interaction like dataZoom, dataView changing. + * `chart.setOption(opt2);` + * Then user press 'reset button' in toolbox. + * + * After doing that all of the interaction effects should be reset, the + * chart should be the same as the result of invoke + * `chart.setOption(opt1); chart.setOption(opt2);`. + * + * Although it is not able ensure that + * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to + * `chart.setOption(merge(opt1, opt2));` exactly, + * this might be the only simple way to implement that feature. + * + * MEMO: We've considered some other approaches: + * 1. Each model handle its self restoration but not uniform treatment. + * (Too complex in logic and error-prone) + * 2. Use a shadow ecModel. (Performace expensive) + */ + function mergeOption(oldOption, newOption) { + newOption = newOption || {}; + + each(newOption, function (newCptOpt, mainType) { + if (newCptOpt == null) { + return; + } + + var oldCptOpt = oldOption[mainType]; + + if (!ComponentModel.hasClass(mainType)) { + oldOption[mainType] = merge(oldCptOpt, newCptOpt, true); + } + else { + newCptOpt = modelUtil.normalizeToArray(newCptOpt); + oldCptOpt = modelUtil.normalizeToArray(oldCptOpt); + + var mapResult = modelUtil.mappingToExists(oldCptOpt, newCptOpt); + + oldOption[mainType] = map(mapResult, function (item) { + return (item.option && item.exist) + ? merge(item.exist, item.option, true) + : (item.exist || item.option); + }); + } + }); + } + + module.exports = OptionManager; + + +/***/ }, +/* 28 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var colorPaletteMixin = __webpack_require__(24); + var env = __webpack_require__(2); + + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var SeriesModel = ComponentModel.extend({ + + type: 'series.__base__', + + /** + * @readOnly + */ + seriesIndex: 0, + + // coodinateSystem will be injected in the echarts/CoordinateSystem + coordinateSystem: null, + + /** + * @type {Object} + * @protected + */ + defaultOption: null, + + /** + * Data provided for legend + * @type {Function} + */ + // PENDING + legendDataProvider: null, + + /** + * Access path of color for visual + */ + visualColorAccessPath: 'itemStyle.normal.color', + + init: function (option, parentModel, ecModel, extraOpt) { + + /** + * @type {number} + * @readOnly + */ + this.seriesIndex = this.componentIndex; + + this.mergeDefaultAndTheme(option, ecModel); + + /** + * @type {module:echarts/data/List|module:echarts/data/Tree|module:echarts/data/Graph} + * @private + */ + this._dataBeforeProcessed = this.getInitialData(option, ecModel); + + // If we reverse the order (make this._data firstly, and then make + // this._dataBeforeProcessed by cloneShallow), cloneShallow will + // cause this._data.graph.data !== this._data when using + // module:echarts/data/Graph or module:echarts/data/Tree. + // See module:echarts/data/helper/linkList + this._data = this._dataBeforeProcessed.cloneShallow(); + }, + + /** + * Util for merge default and theme to option + * @param {Object} option + * @param {module:echarts/model/Global} ecModel + */ + mergeDefaultAndTheme: function (option, ecModel) { + zrUtil.merge( + option, + ecModel.getTheme().get(this.subType) + ); + zrUtil.merge(option, this.getDefaultOption()); + + // Default label emphasis `position` and `show` + // FIXME Set label in mergeOption + modelUtil.defaultEmphasis(option.label, modelUtil.LABEL_OPTIONS); + + this.fillDataTextStyle(option.data); + }, + + mergeOption: function (newSeriesOption, ecModel) { + newSeriesOption = zrUtil.merge(this.option, newSeriesOption, true); + this.fillDataTextStyle(newSeriesOption.data); + + var data = this.getInitialData(newSeriesOption, ecModel); + // TODO Merge data? + if (data) { + this._data = data; + this._dataBeforeProcessed = data.cloneShallow(); + } + }, + + fillDataTextStyle: function (data) { + // Default data label emphasis `position` and `show` + // FIXME Tree structure data ? + // FIXME Performance ? + if (data) { + for (var i = 0; i < data.length; i++) { + if (data[i] && data[i].label) { + modelUtil.defaultEmphasis(data[i].label, modelUtil.LABEL_OPTIONS); + } + } + } + }, + + /** + * Init a data structure from data related option in series + * Must be overwritten + */ + getInitialData: function () {}, + + /** + * @param {string} [dataType] + * @return {module:echarts/data/List} + */ + getData: function (dataType) { + return dataType == null ? this._data : this._data.getLinkedData(dataType); + }, + + /** + * @param {module:echarts/data/List} data + */ + setData: function (data) { + this._data = data; + }, + + /** + * Get data before processed + * @return {module:echarts/data/List} + */ + getRawData: function () { + return this._dataBeforeProcessed; + }, + + /** + * Coord dimension to data dimension. + * + * By default the result is the same as dimensions of series data. + * But in some series data dimensions are different from coord dimensions (i.e. + * candlestick and boxplot). Override this method to handle those cases. + * + * Coord dimension to data dimension can be one-to-many + * + * @param {string} coordDim + * @return {Array.} dimensions on the axis. + */ + coordDimToDataDim: function (coordDim) { + return [coordDim]; + }, + + /** + * Convert data dimension to coord dimension. + * + * @param {string|number} dataDim + * @return {string} + */ + dataDimToCoordDim: function (dataDim) { + return dataDim; + }, + + /** + * Get base axis if has coordinate system and has axis. + * By default use coordSys.getBaseAxis(); + * Can be overrided for some chart. + * @return {type} description + */ + getBaseAxis: function () { + var coordSys = this.coordinateSystem; + return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); + }, + + // FIXME + /** + * Default tooltip formatter + * + * @param {number} dataIndex + * @param {boolean} [multipleSeries=false] + * @param {number} [dataType] + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + function formatArrayValue(value) { + return zrUtil.map(value, function (val, idx) { + var dimInfo = data.getDimensionInfo(idx); + var dimType = dimInfo && dimInfo.type; + if (dimType === 'ordinal') { + return val; + } + else if (dimType === 'time') { + return multipleSeries ? '' : formatUtil.formatTime('yyyy/mm/dd hh:mm:ss', val); + } + else { + return addCommas(val); + } + }).filter(function (val) { + return !!val; + }).join(', '); + } + + var data = this._data; + + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? formatArrayValue(value) : addCommas(value); + var name = data.getName(dataIndex); + var color = data.getItemVisual(dataIndex, 'color'); + var colorEl = ''; + + var seriesName = this.name; + // FIXME + if (seriesName === '\0-') { + // Not show '-' + seriesName = ''; + } + return !multipleSeries + ? ((seriesName && encodeHTML(seriesName) + '
') + colorEl + + (name + ? encodeHTML(name) + ' : ' + formattedValue + : formattedValue) + ) + : (colorEl + encodeHTML(this.name) + ' : ' + formattedValue); + }, + + /** + * @return {boolean} + */ + ifEnableAnimation: function () { + if (env.node) { + return false; + } + + var animationEnabled = this.getShallow('animation'); + if (animationEnabled) { + if (this.getData().count() > this.getShallow('animationThreshold')) { + animationEnabled = false; + } + } + return animationEnabled; + }, + + restoreData: function () { + this._data = this._dataBeforeProcessed.cloneShallow(); + }, + + getColorFromPalette: function (name, scope) { + var ecModel = this.ecModel; + // PENDING + var color = colorPaletteMixin.getColorFromPalette.call(this, name, scope); + if (!color) { + color = ecModel.getColorFromPalette(name, scope); + } + return color; + }, + + getAxisTooltipDataIndex: null + }); + + zrUtil.mixin(SeriesModel, modelUtil.dataFormatMixin); + zrUtil.mixin(SeriesModel, colorPaletteMixin); + + module.exports = SeriesModel; + + +/***/ }, +/* 29 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Group = __webpack_require__(30); + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + + var Component = function () { + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = componentUtil.getUID('viewComponent'); + }; + + Component.prototype = { + + constructor: Component, + + init: function (ecModel, api) {}, + + render: function (componentModel, ecModel, api, payload) {}, + + dispose: function () {} + }; + + var componentProto = Component.prototype; + componentProto.updateView + = componentProto.updateLayout + = componentProto.updateVisual + = function (seriesModel, ecModel, api, payload) { + // Do nothing; + }; + // Enable Component.extend. + clazzUtil.enableClassExtend(Component); + + // Enable capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement(Component, {registerWhenExtend: true}); + + module.exports = Component; + + +/***/ }, +/* 30 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上 + * @module zrender/graphic/Group + * @example + * var Group = require('zrender/lib/container/Group'); + * var Circle = require('zrender/lib/graphic/shape/Circle'); + * var g = new Group(); + * g.position[0] = 100; + * g.position[1] = 100; + * g.add(new Circle({ + * style: { + * x: 100, + * y: 100, + * r: 20, + * } + * })); + * zr.add(g); + */ + + + var zrUtil = __webpack_require__(4); + var Element = __webpack_require__(31); + var BoundingRect = __webpack_require__(9); + + /** + * @alias module:zrender/graphic/Group + * @constructor + * @extends module:zrender/mixin/Transformable + * @extends module:zrender/mixin/Eventful + */ + var Group = function (opts) { + + opts = opts || {}; + + Element.call(this, opts); + + for (var key in opts) { + this[key] = opts[key]; + } + + this._children = []; + + this.__storage = null; + + this.__dirty = true; + }; + + Group.prototype = { + + constructor: Group, + + isGroup: true, + + /** + * @type {string} + */ + type: 'group', + + /** + * 所有子孙元素是否响应鼠标事件 + * @name module:/zrender/container/Group#silent + * @type {boolean} + * @default false + */ + silent: false, + + /** + * @return {Array.} + */ + children: function () { + return this._children.slice(); + }, + + /** + * 获取指定 index 的儿子节点 + * @param {number} idx + * @return {module:zrender/Element} + */ + childAt: function (idx) { + return this._children[idx]; + }, + + /** + * 获取指定名字的儿子节点 + * @param {string} name + * @return {module:zrender/Element} + */ + childOfName: function (name) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + if (children[i].name === name) { + return children[i]; + } + } + }, + + /** + * @return {number} + */ + childCount: function () { + return this._children.length; + }, + + /** + * 添加子节点到最后 + * @param {module:zrender/Element} child + */ + add: function (child) { + if (child && child !== this && child.parent !== this) { + + this._children.push(child); + + this._doAdd(child); + } + + return this; + }, + + /** + * 添加子节点在 nextSibling 之前 + * @param {module:zrender/Element} child + * @param {module:zrender/Element} nextSibling + */ + addBefore: function (child, nextSibling) { + if (child && child !== this && child.parent !== this + && nextSibling && nextSibling.parent === this) { + + var children = this._children; + var idx = children.indexOf(nextSibling); + + if (idx >= 0) { + children.splice(idx, 0, child); + this._doAdd(child); + } + } + + return this; + }, + + _doAdd: function (child) { + if (child.parent) { + child.parent.remove(child); + } + + child.parent = this; + + var storage = this.__storage; + var zr = this.__zr; + if (storage && storage !== child.__storage) { + + storage.addToMap(child); + + if (child instanceof Group) { + child.addChildrenToStorage(storage); + } + } + + zr && zr.refresh(); + }, + + /** + * 移除子节点 + * @param {module:zrender/Element} child + */ + remove: function (child) { + var zr = this.__zr; + var storage = this.__storage; + var children = this._children; + + var idx = zrUtil.indexOf(children, child); + if (idx < 0) { + return this; + } + children.splice(idx, 1); + + child.parent = null; + + if (storage) { + + storage.delFromMap(child.id); + + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + + zr && zr.refresh(); + + return this; + }, + + /** + * 移除所有子节点 + */ + removeAll: function () { + var children = this._children; + var storage = this.__storage; + var child; + var i; + for (i = 0; i < children.length; i++) { + child = children[i]; + if (storage) { + storage.delFromMap(child.id); + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + child.parent = null; + } + children.length = 0; + + return this; + }, + + /** + * 遍历所有子节点 + * @param {Function} cb + * @param {} context + */ + eachChild: function (cb, context) { + var children = this._children; + for (var i = 0; i < children.length; i++) { + var child = children[i]; + cb.call(context, child, i); + } + return this; + }, + + /** + * 深度优先遍历所有子孙节点 + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + cb.call(context, child); + + if (child.type === 'group') { + child.traverse(cb, context); + } + } + return this; + }, + + addChildrenToStorage: function (storage) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + storage.addToMap(child); + if (child instanceof Group) { + child.addChildrenToStorage(storage); + } + } + }, + + delChildrenFromStorage: function (storage) { + for (var i = 0; i < this._children.length; i++) { + var child = this._children[i]; + storage.delFromMap(child.id); + if (child instanceof Group) { + child.delChildrenFromStorage(storage); + } + } + }, + + dirty: function () { + this.__dirty = true; + this.__zr && this.__zr.refresh(); + return this; + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function (includeChildren) { + // TODO Caching + // TODO Transform + var rect = null; + var tmpRect = new BoundingRect(0, 0, 0, 0); + var children = includeChildren || this._children; + var tmpMat = []; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + if (child.ignore || child.invisible) { + continue; + } + + var childRect = child.getBoundingRect(); + var transform = child.getLocalTransform(tmpMat); + if (transform) { + tmpRect.copy(childRect); + tmpRect.applyTransform(transform); + rect = rect || tmpRect.clone(); + rect.union(tmpRect); + } + else { + rect = rect || childRect.clone(); + rect.union(childRect); + } + } + return rect || tmpRect; + } + }; + + zrUtil.inherits(Group, Element); + + module.exports = Group; + + +/***/ }, +/* 31 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module zrender/Element + */ + + + var guid = __webpack_require__(32); + var Eventful = __webpack_require__(33); + var Transformable = __webpack_require__(34); + var Animatable = __webpack_require__(35); + var zrUtil = __webpack_require__(4); + + /** + * @alias module:zrender/Element + * @constructor + * @extends {module:zrender/mixin/Animatable} + * @extends {module:zrender/mixin/Transformable} + * @extends {module:zrender/mixin/Eventful} + */ + var Element = function (opts) { + + Transformable.call(this, opts); + Eventful.call(this, opts); + Animatable.call(this, opts); + + /** + * 画布元素ID + * @type {string} + */ + this.id = opts.id || guid(); + }; + + Element.prototype = { + + /** + * 元素类型 + * Element type + * @type {string} + */ + type: 'element', + + /** + * 元素名字 + * Element name + * @type {string} + */ + name: '', + + /** + * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值 + * ZRender instance will be assigned when element is associated with zrender + * @name module:/zrender/Element#__zr + * @type {module:zrender/ZRender} + */ + __zr: null, + + /** + * 图形是否忽略,为true时忽略图形的绘制以及事件触发 + * If ignore drawing and events of the element object + * @name module:/zrender/Element#ignore + * @type {boolean} + * @default false + */ + ignore: false, + + /** + * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪 + * 该路径会继承被裁减对象的变换 + * @type {module:zrender/graphic/Path} + * @see http://www.w3.org/TR/2dcontext/#clipping-region + * @readOnly + */ + clipPath: null, + + /** + * Drift element + * @param {number} dx dx on the global space + * @param {number} dy dy on the global space + */ + drift: function (dx, dy) { + switch (this.draggable) { + case 'horizontal': + dy = 0; + break; + case 'vertical': + dx = 0; + break; + } + + var m = this.transform; + if (!m) { + m = this.transform = [1, 0, 0, 1, 0, 0]; + } + m[4] += dx; + m[5] += dy; + + this.decomposeTransform(); + this.dirty(false); + }, + + /** + * Hook before update + */ + beforeUpdate: function () {}, + /** + * Hook after update + */ + afterUpdate: function () {}, + /** + * Update each frame + */ + update: function () { + this.updateTransform(); + }, + + /** + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) {}, + + /** + * @protected + */ + attrKV: function (key, value) { + if (key === 'position' || key === 'scale' || key === 'origin') { + // Copy the array + if (value) { + var target = this[key]; + if (!target) { + target = this[key] = []; + } + target[0] = value[0]; + target[1] = value[1]; + } + } + else { + this[key] = value; + } + }, + + /** + * Hide the element + */ + hide: function () { + this.ignore = true; + this.__zr && this.__zr.refresh(); + }, + + /** + * Show the element + */ + show: function () { + this.ignore = false; + this.__zr && this.__zr.refresh(); + }, + + /** + * @param {string|Object} key + * @param {*} value + */ + attr: function (key, value) { + if (typeof key === 'string') { + this.attrKV(key, value); + } + else if (zrUtil.isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.attrKV(name, key[name]); + } + } + } + + this.dirty(false); + + return this; + }, + + /** + * @param {module:zrender/graphic/Path} clipPath + */ + setClipPath: function (clipPath) { + var zr = this.__zr; + if (zr) { + clipPath.addSelfToZr(zr); + } + + // Remove previous clip path + if (this.clipPath && this.clipPath !== clipPath) { + this.removeClipPath(); + } + + this.clipPath = clipPath; + clipPath.__zr = zr; + clipPath.__clipTarget = this; + + this.dirty(false); + }, + + /** + */ + removeClipPath: function () { + var clipPath = this.clipPath; + if (clipPath) { + if (clipPath.__zr) { + clipPath.removeSelfFromZr(clipPath.__zr); + } + + clipPath.__zr = null; + clipPath.__clipTarget = null; + this.clipPath = null; + + this.dirty(false); + } + }, + + /** + * Add self from zrender instance. + * Not recursively because it will be invoked when element added to storage. + * @param {module:zrender/ZRender} zr + */ + addSelfToZr: function (zr) { + this.__zr = zr; + // 添加动画 + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.addAnimator(animators[i]); + } + } + + if (this.clipPath) { + this.clipPath.addSelfToZr(zr); + } + }, + + /** + * Remove self from zrender instance. + * Not recursively because it will be invoked when element added to storage. + * @param {module:zrender/ZRender} zr + */ + removeSelfFromZr: function (zr) { + this.__zr = null; + // 移除动画 + var animators = this.animators; + if (animators) { + for (var i = 0; i < animators.length; i++) { + zr.animation.removeAnimator(animators[i]); + } + } + + if (this.clipPath) { + this.clipPath.removeSelfFromZr(zr); + } + } + }; + + zrUtil.mixin(Element, Animatable); + zrUtil.mixin(Element, Transformable); + zrUtil.mixin(Element, Eventful); + + module.exports = Element; + + +/***/ }, +/* 32 */ +/***/ function(module, exports) { + + /** + * zrender: 生成唯一id + * + * @author errorrik (errorrik@gmail.com) + */ + + + var idStart = 0x0907; + + module.exports = function () { + return idStart++; + }; + + + +/***/ }, +/* 33 */ +/***/ function(module, exports) { + + /** + * 事件扩展 + * @module zrender/mixin/Eventful + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * pissang (https://www.github.com/pissang) + */ + + + var arrySlice = Array.prototype.slice; + + /** + * 事件分发器 + * @alias module:zrender/mixin/Eventful + * @constructor + */ + var Eventful = function () { + this._$handlers = {}; + }; + + Eventful.prototype = { + + constructor: Eventful, + + /** + * 单次触发绑定,trigger后销毁 + * + * @param {string} event 事件名 + * @param {Function} handler 响应函数 + * @param {Object} context + */ + one: function (event, handler, context) { + var _h = this._$handlers; + + if (!handler || !event) { + return this; + } + + if (!_h[event]) { + _h[event] = []; + } + + for (var i = 0; i < _h[event].length; i++) { + if (_h[event][i].h === handler) { + return this; + } + } + + _h[event].push({ + h: handler, + one: true, + ctx: context || this + }); + + return this; + }, + + /** + * 绑定事件 + * @param {string} event 事件名 + * @param {Function} handler 事件处理函数 + * @param {Object} [context] + */ + on: function (event, handler, context) { + var _h = this._$handlers; + + if (!handler || !event) { + return this; + } + + if (!_h[event]) { + _h[event] = []; + } + + for (var i = 0; i < _h[event].length; i++) { + if (_h[event][i].h === handler) { + return this; + } + } + + _h[event].push({ + h: handler, + one: false, + ctx: context || this + }); + + return this; + }, + + /** + * 是否绑定了事件 + * @param {string} event + * @return {boolean} + */ + isSilent: function (event) { + var _h = this._$handlers; + return _h[event] && _h[event].length; + }, + + /** + * 解绑事件 + * @param {string} event 事件名 + * @param {Function} [handler] 事件处理函数 + */ + off: function (event, handler) { + var _h = this._$handlers; + + if (!event) { + this._$handlers = {}; + return this; + } + + if (handler) { + if (_h[event]) { + var newList = []; + for (var i = 0, l = _h[event].length; i < l; i++) { + if (_h[event][i]['h'] != handler) { + newList.push(_h[event][i]); + } + } + _h[event] = newList; + } + + if (_h[event] && _h[event].length === 0) { + delete _h[event]; + } + } + else { + delete _h[event]; + } + + return this; + }, + + /** + * 事件分发 + * + * @param {string} type 事件类型 + */ + trigger: function (type) { + if (this._$handlers[type]) { + var args = arguments; + var argLen = args.length; + + if (argLen > 3) { + args = arrySlice.call(args, 1); + } + + var _h = this._$handlers[type]; + var len = _h.length; + for (var i = 0; i < len;) { + // Optimize advise from backbone + switch (argLen) { + case 1: + _h[i]['h'].call(_h[i]['ctx']); + break; + case 2: + _h[i]['h'].call(_h[i]['ctx'], args[1]); + break; + case 3: + _h[i]['h'].call(_h[i]['ctx'], args[1], args[2]); + break; + default: + // have more than 2 given arguments + _h[i]['h'].apply(_h[i]['ctx'], args); + break; + } + + if (_h[i]['one']) { + _h.splice(i, 1); + len--; + } + else { + i++; + } + } + } + + return this; + }, + + /** + * 带有context的事件分发, 最后一个参数是事件回调的context + * @param {string} type 事件类型 + */ + triggerWithContext: function (type) { + if (this._$handlers[type]) { + var args = arguments; + var argLen = args.length; + + if (argLen > 4) { + args = arrySlice.call(args, 1, args.length - 1); + } + var ctx = args[args.length - 1]; + + var _h = this._$handlers[type]; + var len = _h.length; + for (var i = 0; i < len;) { + // Optimize advise from backbone + switch (argLen) { + case 1: + _h[i]['h'].call(ctx); + break; + case 2: + _h[i]['h'].call(ctx, args[1]); + break; + case 3: + _h[i]['h'].call(ctx, args[1], args[2]); + break; + default: + // have more than 2 given arguments + _h[i]['h'].apply(ctx, args); + break; + } + + if (_h[i]['one']) { + _h.splice(i, 1); + len--; + } + else { + i++; + } + } + } + + return this; + } + }; + + // 对象可以通过 onxxxx 绑定事件 + /** + * @event module:zrender/mixin/Eventful#onclick + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseover + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseout + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousemove + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousewheel + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmousedown + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#onmouseup + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragstart + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragend + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragenter + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragleave + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondragover + * @type {Function} + * @default null + */ + /** + * @event module:zrender/mixin/Eventful#ondrop + * @type {Function} + * @default null + */ + + module.exports = Eventful; + + + +/***/ }, +/* 34 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 提供变换扩展 + * @module zrender/mixin/Transformable + * @author pissang (https://www.github.com/pissang) + */ + + + var matrix = __webpack_require__(11); + var vector = __webpack_require__(10); + var mIdentity = matrix.identity; + + var EPSILON = 5e-5; + + function isNotAroundZero(val) { + return val > EPSILON || val < -EPSILON; + } + + /** + * @alias module:zrender/mixin/Transformable + * @constructor + */ + var Transformable = function (opts) { + opts = opts || {}; + // If there are no given position, rotation, scale + if (!opts.position) { + /** + * 平移 + * @type {Array.} + * @default [0, 0] + */ + this.position = [0, 0]; + } + if (opts.rotation == null) { + /** + * 旋转 + * @type {Array.} + * @default 0 + */ + this.rotation = 0; + } + if (!opts.scale) { + /** + * 缩放 + * @type {Array.} + * @default [1, 1] + */ + this.scale = [1, 1]; + } + /** + * 旋转和缩放的原点 + * @type {Array.} + * @default null + */ + this.origin = this.origin || null; + }; + + var transformableProto = Transformable.prototype; + transformableProto.transform = null; + + /** + * 判断是否需要有坐标变换 + * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵 + */ + transformableProto.needLocalTransform = function () { + return isNotAroundZero(this.rotation) + || isNotAroundZero(this.position[0]) + || isNotAroundZero(this.position[1]) + || isNotAroundZero(this.scale[0] - 1) + || isNotAroundZero(this.scale[1] - 1); + }; + + transformableProto.updateTransform = function () { + var parent = this.parent; + var parentHasTransform = parent && parent.transform; + var needLocalTransform = this.needLocalTransform(); + + var m = this.transform; + if (!(needLocalTransform || parentHasTransform)) { + m && mIdentity(m); + return; + } + + m = m || matrix.create(); + + if (needLocalTransform) { + this.getLocalTransform(m); + } + else { + mIdentity(m); + } + + // 应用父节点变换 + if (parentHasTransform) { + if (needLocalTransform) { + matrix.mul(m, parent.transform, m); + } + else { + matrix.copy(m, parent.transform); + } + } + // 保存这个变换矩阵 + this.transform = m; + + this.invTransform = this.invTransform || matrix.create(); + matrix.invert(this.invTransform, m); + }; + + transformableProto.getLocalTransform = function (m) { + m = m || []; + mIdentity(m); + + var origin = this.origin; + + var scale = this.scale; + var rotation = this.rotation; + var position = this.position; + if (origin) { + // Translate to origin + m[4] -= origin[0]; + m[5] -= origin[1]; + } + matrix.scale(m, m, scale); + if (rotation) { + matrix.rotate(m, m, rotation); + } + if (origin) { + // Translate back from origin + m[4] += origin[0]; + m[5] += origin[1]; + } + + m[4] += position[0]; + m[5] += position[1]; + + return m; + }; + /** + * 将自己的transform应用到context上 + * @param {Context2D} ctx + */ + transformableProto.setTransform = function (ctx) { + var m = this.transform; + var dpr = ctx.dpr || 1; + if (m) { + ctx.setTransform(dpr * m[0], dpr * m[1], dpr * m[2], dpr * m[3], dpr * m[4], dpr * m[5]); + } + else { + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + }; + + transformableProto.restoreTransform = function (ctx) { + var m = this.transform; + var dpr = ctx.dpr || 1; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + var tmpTransform = []; + + /** + * 分解`transform`矩阵到`position`, `rotation`, `scale` + */ + transformableProto.decomposeTransform = function () { + if (!this.transform) { + return; + } + var parent = this.parent; + var m = this.transform; + if (parent && parent.transform) { + // Get local transform and decompose them to position, scale, rotation + matrix.mul(tmpTransform, parent.invTransform, m); + m = tmpTransform; + } + var sx = m[0] * m[0] + m[1] * m[1]; + var sy = m[2] * m[2] + m[3] * m[3]; + var position = this.position; + var scale = this.scale; + if (isNotAroundZero(sx - 1)) { + sx = Math.sqrt(sx); + } + if (isNotAroundZero(sy - 1)) { + sy = Math.sqrt(sy); + } + if (m[0] < 0) { + sx = -sx; + } + if (m[3] < 0) { + sy = -sy; + } + position[0] = m[4]; + position[1] = m[5]; + scale[0] = sx; + scale[1] = sy; + this.rotation = Math.atan2(-m[1] / sy, m[0] / sx); + }; + + /** + * Get global scale + * @return {Array.} + */ + transformableProto.getGlobalScale = function () { + var m = this.transform; + if (!m) { + return [1, 1]; + } + var sx = Math.sqrt(m[0] * m[0] + m[1] * m[1]); + var sy = Math.sqrt(m[2] * m[2] + m[3] * m[3]); + if (m[0] < 0) { + sx = -sx; + } + if (m[3] < 0) { + sy = -sy; + } + return [sx, sy]; + }; + /** + * 变换坐标位置到 shape 的局部坐标空间 + * @method + * @param {number} x + * @param {number} y + * @return {Array.} + */ + transformableProto.transformCoordToLocal = function (x, y) { + var v2 = [x, y]; + var invTransform = this.invTransform; + if (invTransform) { + vector.applyTransform(v2, v2, invTransform); + } + return v2; + }; + + /** + * 变换局部坐标位置到全局坐标空间 + * @method + * @param {number} x + * @param {number} y + * @return {Array.} + */ + transformableProto.transformCoordToGlobal = function (x, y) { + var v2 = [x, y]; + var transform = this.transform; + if (transform) { + vector.applyTransform(v2, v2, transform); + } + return v2; + }; + + module.exports = Transformable; + + + +/***/ }, +/* 35 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module zrender/mixin/Animatable + */ + + + var Animator = __webpack_require__(36); + var util = __webpack_require__(4); + var isString = util.isString; + var isFunction = util.isFunction; + var isObject = util.isObject; + var log = __webpack_require__(40); + + /** + * @alias modue:zrender/mixin/Animatable + * @constructor + */ + var Animatable = function () { + + /** + * @type {Array.} + * @readOnly + */ + this.animators = []; + }; + + Animatable.prototype = { + + constructor: Animatable, + + /** + * 动画 + * + * @param {string} path 需要添加动画的属性获取路径,可以通过a.b.c来获取深层的属性 + * @param {boolean} [loop] 动画是否循环 + * @return {module:zrender/animation/Animator} + * @example: + * el.animate('style', false) + * .when(1000, {x: 10} ) + * .done(function(){ // Animation done }) + * .start() + */ + animate: function (path, loop) { + var target; + var animatingShape = false; + var el = this; + var zr = this.__zr; + if (path) { + var pathSplitted = path.split('.'); + var prop = el; + // If animating shape + animatingShape = pathSplitted[0] === 'shape'; + for (var i = 0, l = pathSplitted.length; i < l; i++) { + if (!prop) { + continue; + } + prop = prop[pathSplitted[i]]; + } + if (prop) { + target = prop; + } + } + else { + target = el; + } + + if (!target) { + log( + 'Property "' + + path + + '" is not existed in element ' + + el.id + ); + return; + } + + var animators = el.animators; + + var animator = new Animator(target, loop); + + animator.during(function (target) { + el.dirty(animatingShape); + }) + .done(function () { + // FIXME Animator will not be removed if use `Animator#stop` to stop animation + animators.splice(util.indexOf(animators, animator), 1); + }); + + animators.push(animator); + + // If animate after added to the zrender + if (zr) { + zr.animation.addAnimator(animator); + } + + return animator; + }, + + /** + * 停止动画 + * @param {boolean} forwardToLast If move to last frame before stop + */ + stopAnimation: function (forwardToLast) { + var animators = this.animators; + var len = animators.length; + for (var i = 0; i < len; i++) { + animators[i].stop(forwardToLast); + } + animators.length = 0; + + return this; + }, + + /** + * @param {Object} target + * @param {number} [time=500] Time in ms + * @param {string} [easing='linear'] + * @param {number} [delay=0] + * @param {Function} [callback] + * + * @example + * // Animate position + * el.animateTo({ + * position: [10, 10] + * }, function () { // done }) + * + * // Animate shape, style and position in 100ms, delayed 100ms, with cubicOut easing + * el.animateTo({ + * shape: { + * width: 500 + * }, + * style: { + * fill: 'red' + * } + * position: [10, 10] + * }, 100, 100, 'cubicOut', function () { // done }) + */ + // TODO Return animation key + animateTo: function (target, time, delay, easing, callback) { + // animateTo(target, time, easing, callback); + if (isString(delay)) { + callback = easing; + easing = delay; + delay = 0; + } + // animateTo(target, time, delay, callback); + else if (isFunction(easing)) { + callback = easing; + easing = 'linear'; + delay = 0; + } + // animateTo(target, time, callback); + else if (isFunction(delay)) { + callback = delay; + delay = 0; + } + // animateTo(target, callback) + else if (isFunction(time)) { + callback = time; + time = 500; + } + // animateTo(target) + else if (!time) { + time = 500; + } + // Stop all previous animations + this.stopAnimation(); + this._animateToShallow('', this, target, time, delay, easing, callback); + + // Animators may be removed immediately after start + // if there is nothing to animate + var animators = this.animators.slice(); + var count = animators.length; + function done() { + count--; + if (!count) { + callback && callback(); + } + } + + // No animators. This should be checked before animators[i].start(), + // because 'done' may be executed immediately if no need to animate. + if (!count) { + callback && callback(); + } + // Start after all animators created + // Incase any animator is done immediately when all animation properties are not changed + for (var i = 0; i < animators.length; i++) { + animators[i] + .done(done) + .start(easing); + } + }, + + /** + * @private + * @param {string} path='' + * @param {Object} source=this + * @param {Object} target + * @param {number} [time=500] + * @param {number} [delay=0] + * + * @example + * // Animate position + * el._animateToShallow({ + * position: [10, 10] + * }) + * + * // Animate shape, style and position in 100ms, delayed 100ms + * el._animateToShallow({ + * shape: { + * width: 500 + * }, + * style: { + * fill: 'red' + * } + * position: [10, 10] + * }, 100, 100) + */ + _animateToShallow: function (path, source, target, time, delay) { + var objShallow = {}; + var propertyCount = 0; + for (var name in target) { + if (source[name] != null) { + if (isObject(target[name]) && !util.isArrayLike(target[name])) { + this._animateToShallow( + path ? path + '.' + name : name, + source[name], + target[name], + time, + delay + ); + } + else { + objShallow[name] = target[name]; + propertyCount++; + } + } + else if (target[name] != null) { + // Attr directly if not has property + // FIXME, if some property not needed for element ? + if (!path) { + this.attr(name, target[name]); + } + else { // Shape or style + var props = {}; + props[path] = {}; + props[path][name] = target[name]; + this.attr(props); + } + } + } + + if (propertyCount > 0) { + this.animate(path, false) + .when(time == null ? 500 : time, objShallow) + .delay(delay || 0); + } + + return this; + } + }; + + module.exports = Animatable; + + +/***/ }, +/* 36 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/animation/Animator + */ + + + var Clip = __webpack_require__(37); + var color = __webpack_require__(39); + var util = __webpack_require__(4); + var isArrayLike = util.isArrayLike; + + var arraySlice = Array.prototype.slice; + + function defaultGetter(target, key) { + return target[key]; + } + + function defaultSetter(target, key, value) { + target[key] = value; + } + + /** + * @param {number} p0 + * @param {number} p1 + * @param {number} percent + * @return {number} + */ + function interpolateNumber(p0, p1, percent) { + return (p1 - p0) * percent + p0; + } + + /** + * @param {string} p0 + * @param {string} p1 + * @param {number} percent + * @return {string} + */ + function interpolateString(p0, p1, percent) { + return percent > 0.5 ? p1 : p0; + } + + /** + * @param {Array} p0 + * @param {Array} p1 + * @param {number} percent + * @param {Array} out + * @param {number} arrDim + */ + function interpolateArray(p0, p1, percent, out, arrDim) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = interpolateNumber(p0[i], p1[i], percent); + } + } + else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = interpolateNumber( + p0[i][j], p1[i][j], percent + ); + } + } + } + } + + // arr0 is source array, arr1 is target array. + // Do some preprocess to avoid error happened when interpolating from arr0 to arr1 + function fillArr(arr0, arr1, arrDim) { + var arr0Len = arr0.length; + var arr1Len = arr1.length; + if (arr0Len !== arr1Len) { + // FIXME Not work for TypedArray + var isPreviousLarger = arr0Len > arr1Len; + if (isPreviousLarger) { + // Cut the previous + arr0.length = arr1Len; + } + else { + // Fill the previous + for (var i = arr0Len; i < arr1Len; i++) { + arr0.push( + arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i]) + ); + } + } + } + // Handling NaN value + var len2 = arr0[0] && arr0[0].length; + for (var i = 0; i < arr0.length; i++) { + if (arrDim === 1) { + if (isNaN(arr0[i])) { + arr0[i] = arr1[i]; + } + } + else { + for (var j = 0; j < len2; j++) { + if (isNaN(arr0[i][j])) { + arr0[i][j] = arr1[i][j]; + } + } + } + } + } + + /** + * @param {Array} arr0 + * @param {Array} arr1 + * @param {number} arrDim + * @return {boolean} + */ + function isArraySame(arr0, arr1, arrDim) { + if (arr0 === arr1) { + return true; + } + var len = arr0.length; + if (len !== arr1.length) { + return false; + } + if (arrDim === 1) { + for (var i = 0; i < len; i++) { + if (arr0[i] !== arr1[i]) { + return false; + } + } + } + else { + var len2 = arr0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + if (arr0[i][j] !== arr1[i][j]) { + return false; + } + } + } + } + return true; + } + + /** + * Catmull Rom interpolate array + * @param {Array} p0 + * @param {Array} p1 + * @param {Array} p2 + * @param {Array} p3 + * @param {number} t + * @param {number} t2 + * @param {number} t3 + * @param {Array} out + * @param {number} arrDim + */ + function catmullRomInterpolateArray( + p0, p1, p2, p3, t, t2, t3, out, arrDim + ) { + var len = p0.length; + if (arrDim == 1) { + for (var i = 0; i < len; i++) { + out[i] = catmullRomInterpolate( + p0[i], p1[i], p2[i], p3[i], t, t2, t3 + ); + } + } + else { + var len2 = p0[0].length; + for (var i = 0; i < len; i++) { + for (var j = 0; j < len2; j++) { + out[i][j] = catmullRomInterpolate( + p0[i][j], p1[i][j], p2[i][j], p3[i][j], + t, t2, t3 + ); + } + } + } + } + + /** + * Catmull Rom interpolate number + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @param {number} t2 + * @param {number} t3 + * @return {number} + */ + function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + return (2 * (p1 - p2) + v0 + v1) * t3 + + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + + v0 * t + p1; + } + + function cloneValue(value) { + if (isArrayLike(value)) { + var len = value.length; + if (isArrayLike(value[0])) { + var ret = []; + for (var i = 0; i < len; i++) { + ret.push(arraySlice.call(value[i])); + } + return ret; + } + + return arraySlice.call(value); + } + + return value; + } + + function rgba2String(rgba) { + rgba[0] = Math.floor(rgba[0]); + rgba[1] = Math.floor(rgba[1]); + rgba[2] = Math.floor(rgba[2]); + + return 'rgba(' + rgba.join(',') + ')'; + } + + function createTrackClip (animator, easing, oneTrackDone, keyframes, propName) { + var getter = animator._getter; + var setter = animator._setter; + var useSpline = easing === 'spline'; + + var trackLen = keyframes.length; + if (!trackLen) { + return; + } + // Guess data type + var firstVal = keyframes[0].value; + var isValueArray = isArrayLike(firstVal); + var isValueColor = false; + var isValueString = false; + + // For vertices morphing + var arrDim = ( + isValueArray + && isArrayLike(firstVal[0]) + ) + ? 2 : 1; + var trackMaxTime; + // Sort keyframe as ascending + keyframes.sort(function(a, b) { + return a.time - b.time; + }); + + trackMaxTime = keyframes[trackLen - 1].time; + // Percents of each keyframe + var kfPercents = []; + // Value of each keyframe + var kfValues = []; + var prevValue = keyframes[0].value; + var isAllValueEqual = true; + for (var i = 0; i < trackLen; i++) { + kfPercents.push(keyframes[i].time / trackMaxTime); + // Assume value is a color when it is a string + var value = keyframes[i].value; + + // Check if value is equal, deep check if value is array + if (!((isValueArray && isArraySame(value, prevValue, arrDim)) + || (!isValueArray && value === prevValue))) { + isAllValueEqual = false; + } + prevValue = value; + + // Try converting a string to a color array + if (typeof value == 'string') { + var colorArray = color.parse(value); + if (colorArray) { + value = colorArray; + isValueColor = true; + } + else { + isValueString = true; + } + } + kfValues.push(value); + } + if (isAllValueEqual) { + return; + } + + var lastValue = kfValues[trackLen - 1]; + // Polyfill array and NaN value + for (var i = 0; i < trackLen - 1; i++) { + if (isValueArray) { + fillArr(kfValues[i], lastValue, arrDim); + } + else { + if (isNaN(kfValues[i]) && !isNaN(lastValue) && !isValueString && !isValueColor) { + kfValues[i] = lastValue; + } + } + } + isValueArray && fillArr(getter(animator._target, propName), lastValue, arrDim); + + // Cache the key of last frame to speed up when + // animation playback is sequency + var lastFrame = 0; + var lastFramePercent = 0; + var start; + var w; + var p0; + var p1; + var p2; + var p3; + + if (isValueColor) { + var rgba = [0, 0, 0, 0]; + } + + var onframe = function (target, percent) { + // Find the range keyframes + // kf1-----kf2---------current--------kf3 + // find kf2 and kf3 and do interpolation + var frame; + // In the easing function like elasticOut, percent may less than 0 + if (percent < 0) { + frame = 0; + } + else if (percent < lastFramePercent) { + // Start from next key + // PENDING start from lastFrame ? + start = Math.min(lastFrame + 1, trackLen - 1); + for (frame = start; frame >= 0; frame--) { + if (kfPercents[frame] <= percent) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, trackLen - 2); + } + else { + for (frame = lastFrame; frame < trackLen; frame++) { + if (kfPercents[frame] > percent) { + break; + } + } + frame = Math.min(frame - 1, trackLen - 2); + } + lastFrame = frame; + lastFramePercent = percent; + + var range = (kfPercents[frame + 1] - kfPercents[frame]); + if (range === 0) { + return; + } + else { + w = (percent - kfPercents[frame]) / range; + } + if (useSpline) { + p1 = kfValues[frame]; + p0 = kfValues[frame === 0 ? frame : frame - 1]; + p2 = kfValues[frame > trackLen - 2 ? trackLen - 1 : frame + 1]; + p3 = kfValues[frame > trackLen - 3 ? trackLen - 1 : frame + 2]; + if (isValueArray) { + catmullRomInterpolateArray( + p0, p1, p2, p3, w, w * w, w * w * w, + getter(target, propName), + arrDim + ); + } + else { + var value; + if (isValueColor) { + value = catmullRomInterpolateArray( + p0, p1, p2, p3, w, w * w, w * w * w, + rgba, 1 + ); + value = rgba2String(rgba); + } + else if (isValueString) { + // String is step(0.5) + return interpolateString(p1, p2, w); + } + else { + value = catmullRomInterpolate( + p0, p1, p2, p3, w, w * w, w * w * w + ); + } + setter( + target, + propName, + value + ); + } + } + else { + if (isValueArray) { + interpolateArray( + kfValues[frame], kfValues[frame + 1], w, + getter(target, propName), + arrDim + ); + } + else { + var value; + if (isValueColor) { + interpolateArray( + kfValues[frame], kfValues[frame + 1], w, + rgba, 1 + ); + value = rgba2String(rgba); + } + else if (isValueString) { + // String is step(0.5) + return interpolateString(kfValues[frame], kfValues[frame + 1], w); + } + else { + value = interpolateNumber(kfValues[frame], kfValues[frame + 1], w); + } + setter( + target, + propName, + value + ); + } + } + }; + + var clip = new Clip({ + target: animator._target, + life: trackMaxTime, + loop: animator._loop, + delay: animator._delay, + onframe: onframe, + ondestroy: oneTrackDone + }); + + if (easing && easing !== 'spline') { + clip.easing = easing; + } + + return clip; + } + + /** + * @alias module:zrender/animation/Animator + * @constructor + * @param {Object} target + * @param {boolean} loop + * @param {Function} getter + * @param {Function} setter + */ + var Animator = function(target, loop, getter, setter) { + this._tracks = {}; + this._target = target; + + this._loop = loop || false; + + this._getter = getter || defaultGetter; + this._setter = setter || defaultSetter; + + this._clipCount = 0; + + this._delay = 0; + + this._doneList = []; + + this._onframeList = []; + + this._clipList = []; + }; + + Animator.prototype = { + /** + * 设置动画关键帧 + * @param {number} time 关键帧时间,单位是ms + * @param {Object} props 关键帧的属性值,key-value表示 + * @return {module:zrender/animation/Animator} + */ + when: function(time /* ms */, props) { + var tracks = this._tracks; + for (var propName in props) { + if (!tracks[propName]) { + tracks[propName] = []; + // Invalid value + var value = this._getter(this._target, propName); + if (value == null) { + // zrLog('Invalid property ' + propName); + continue; + } + // If time is 0 + // Then props is given initialize value + // Else + // Initialize value from current prop value + if (time !== 0) { + tracks[propName].push({ + time: 0, + value: cloneValue(value) + }); + } + } + tracks[propName].push({ + time: time, + value: props[propName] + }); + } + return this; + }, + /** + * 添加动画每一帧的回调函数 + * @param {Function} callback + * @return {module:zrender/animation/Animator} + */ + during: function (callback) { + this._onframeList.push(callback); + return this; + }, + + _doneCallback: function () { + // Clear all tracks + this._tracks = {}; + // Clear all clips + this._clipList.length = 0; + + var doneList = this._doneList; + var len = doneList.length; + for (var i = 0; i < len; i++) { + doneList[i].call(this); + } + }, + /** + * 开始执行动画 + * @param {string|Function} easing + * 动画缓动函数,详见{@link module:zrender/animation/easing} + * @return {module:zrender/animation/Animator} + */ + start: function (easing) { + + var self = this; + var clipCount = 0; + + var oneTrackDone = function() { + clipCount--; + if (!clipCount) { + self._doneCallback(); + } + }; + + var lastClip; + for (var propName in this._tracks) { + var clip = createTrackClip( + this, easing, oneTrackDone, + this._tracks[propName], propName + ); + if (clip) { + this._clipList.push(clip); + clipCount++; + + // If start after added to animation + if (this.animation) { + this.animation.addClip(clip); + } + + lastClip = clip; + } + } + + // Add during callback on the last clip + if (lastClip) { + var oldOnFrame = lastClip.onframe; + lastClip.onframe = function (target, percent) { + oldOnFrame(target, percent); + + for (var i = 0; i < self._onframeList.length; i++) { + self._onframeList[i](target, percent); + } + }; + } + + if (!clipCount) { + this._doneCallback(); + } + return this; + }, + /** + * 停止动画 + * @param {boolean} forwardToLast If move to last frame before stop + */ + stop: function (forwardToLast) { + var clipList = this._clipList; + var animation = this.animation; + for (var i = 0; i < clipList.length; i++) { + var clip = clipList[i]; + if (forwardToLast) { + // Move to last frame before stop + clip.onframe(this._target, 1); + } + animation && animation.removeClip(clip); + } + clipList.length = 0; + }, + /** + * 设置动画延迟开始的时间 + * @param {number} time 单位ms + * @return {module:zrender/animation/Animator} + */ + delay: function (time) { + this._delay = time; + return this; + }, + /** + * 添加动画结束的回调 + * @param {Function} cb + * @return {module:zrender/animation/Animator} + */ + done: function(cb) { + if (cb) { + this._doneList.push(cb); + } + return this; + }, + + /** + * @return {Array.} + */ + getClips: function () { + return this._clipList; + } + }; + + module.exports = Animator; + + +/***/ }, +/* 37 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 动画主控制器 + * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件 + * @config life(1000) 动画时长 + * @config delay(0) 动画延迟时间 + * @config loop(true) + * @config gap(0) 循环的间隔时间 + * @config onframe + * @config easing(optional) + * @config ondestroy(optional) + * @config onrestart(optional) + * + * TODO pause + */ + + + var easingFuncs = __webpack_require__(38); + + function Clip(options) { + + this._target = options.target; + + // 生命周期 + this._life = options.life || 1000; + // 延时 + this._delay = options.delay || 0; + // 开始时间 + // this._startTime = new Date().getTime() + this._delay;// 单位毫秒 + this._initialized = false; + + // 是否循环 + this.loop = options.loop == null ? false : options.loop; + + this.gap = options.gap || 0; + + this.easing = options.easing || 'Linear'; + + this.onframe = options.onframe; + this.ondestroy = options.ondestroy; + this.onrestart = options.onrestart; + } + + Clip.prototype = { + + constructor: Clip, + + step: function (globalTime) { + // Set startTime on first step, or _startTime may has milleseconds different between clips + // PENDING + if (!this._initialized) { + this._startTime = globalTime + this._delay; + this._initialized = true; + } + + var percent = (globalTime - this._startTime) / this._life; + + // 还没开始 + if (percent < 0) { + return; + } + + percent = Math.min(percent, 1); + + var easing = this.easing; + var easingFunc = typeof easing == 'string' ? easingFuncs[easing] : easing; + var schedule = typeof easingFunc === 'function' + ? easingFunc(percent) + : percent; + + this.fire('frame', schedule); + + // 结束 + if (percent == 1) { + if (this.loop) { + this.restart (globalTime); + // 重新开始周期 + // 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件 + return 'restart'; + } + + // 动画完成将这个控制器标识为待删除 + // 在Animation.update中进行批量删除 + this._needsRemove = true; + return 'destroy'; + } + + return null; + }, + + restart: function (globalTime) { + var remainder = (globalTime - this._startTime) % this._life; + this._startTime = globalTime - remainder + this.gap; + + this._needsRemove = false; + }, + + fire: function(eventType, arg) { + eventType = 'on' + eventType; + if (this[eventType]) { + this[eventType](this._target, arg); + } + } + }; + + module.exports = Clip; + + + +/***/ }, +/* 38 */ +/***/ function(module, exports) { + + /** + * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js + * @see http://sole.github.io/tween.js/examples/03_graphs.html + * @exports zrender/animation/easing + */ + + var easing = { + /** + * @param {number} k + * @return {number} + */ + linear: function (k) { + return k; + }, + + /** + * @param {number} k + * @return {number} + */ + quadraticIn: function (k) { + return k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quadraticOut: function (k) { + return k * (2 - k); + }, + /** + * @param {number} k + * @return {number} + */ + quadraticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k; + } + return -0.5 * (--k * (k - 2) - 1); + }, + + // 三次方的缓动(t^3) + /** + * @param {number} k + * @return {number} + */ + cubicIn: function (k) { + return k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + cubicOut: function (k) { + return --k * k * k + 1; + }, + /** + * @param {number} k + * @return {number} + */ + cubicInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k; + } + return 0.5 * ((k -= 2) * k * k + 2); + }, + + // 四次方的缓动(t^4) + /** + * @param {number} k + * @return {number} + */ + quarticIn: function (k) { + return k * k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quarticOut: function (k) { + return 1 - (--k * k * k * k); + }, + /** + * @param {number} k + * @return {number} + */ + quarticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k; + } + return -0.5 * ((k -= 2) * k * k * k - 2); + }, + + // 五次方的缓动(t^5) + /** + * @param {number} k + * @return {number} + */ + quinticIn: function (k) { + return k * k * k * k * k; + }, + /** + * @param {number} k + * @return {number} + */ + quinticOut: function (k) { + return --k * k * k * k * k + 1; + }, + /** + * @param {number} k + * @return {number} + */ + quinticInOut: function (k) { + if ((k *= 2) < 1) { + return 0.5 * k * k * k * k * k; + } + return 0.5 * ((k -= 2) * k * k * k * k + 2); + }, + + // 正弦曲线的缓动(sin(t)) + /** + * @param {number} k + * @return {number} + */ + sinusoidalIn: function (k) { + return 1 - Math.cos(k * Math.PI / 2); + }, + /** + * @param {number} k + * @return {number} + */ + sinusoidalOut: function (k) { + return Math.sin(k * Math.PI / 2); + }, + /** + * @param {number} k + * @return {number} + */ + sinusoidalInOut: function (k) { + return 0.5 * (1 - Math.cos(Math.PI * k)); + }, + + // 指数曲线的缓动(2^t) + /** + * @param {number} k + * @return {number} + */ + exponentialIn: function (k) { + return k === 0 ? 0 : Math.pow(1024, k - 1); + }, + /** + * @param {number} k + * @return {number} + */ + exponentialOut: function (k) { + return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); + }, + /** + * @param {number} k + * @return {number} + */ + exponentialInOut: function (k) { + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if ((k *= 2) < 1) { + return 0.5 * Math.pow(1024, k - 1); + } + return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2); + }, + + // 圆形曲线的缓动(sqrt(1-t^2)) + /** + * @param {number} k + * @return {number} + */ + circularIn: function (k) { + return 1 - Math.sqrt(1 - k * k); + }, + /** + * @param {number} k + * @return {number} + */ + circularOut: function (k) { + return Math.sqrt(1 - (--k * k)); + }, + /** + * @param {number} k + * @return {number} + */ + circularInOut: function (k) { + if ((k *= 2) < 1) { + return -0.5 * (Math.sqrt(1 - k * k) - 1); + } + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); + }, + + // 创建类似于弹簧在停止前来回振荡的动画 + /** + * @param {number} k + * @return {number} + */ + elasticIn: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return -(a * Math.pow(2, 10 * (k -= 1)) * + Math.sin((k - s) * (2 * Math.PI) / p)); + }, + /** + * @param {number} k + * @return {number} + */ + elasticOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + return (a * Math.pow(2, -10 * k) * + Math.sin((k - s) * (2 * Math.PI) / p) + 1); + }, + /** + * @param {number} k + * @return {number} + */ + elasticInOut: function (k) { + var s; + var a = 0.1; + var p = 0.4; + if (k === 0) { + return 0; + } + if (k === 1) { + return 1; + } + if (!a || a < 1) { + a = 1; s = p / 4; + } + else { + s = p * Math.asin(1 / a) / (2 * Math.PI); + } + if ((k *= 2) < 1) { + return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p)); + } + return a * Math.pow(2, -10 * (k -= 1)) + * Math.sin((k - s) * (2 * Math.PI) / p) * 0.5 + 1; + + }, + + // 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动 + /** + * @param {number} k + * @return {number} + */ + backIn: function (k) { + var s = 1.70158; + return k * k * ((s + 1) * k - s); + }, + /** + * @param {number} k + * @return {number} + */ + backOut: function (k) { + var s = 1.70158; + return --k * k * ((s + 1) * k + s) + 1; + }, + /** + * @param {number} k + * @return {number} + */ + backInOut: function (k) { + var s = 1.70158 * 1.525; + if ((k *= 2) < 1) { + return 0.5 * (k * k * ((s + 1) * k - s)); + } + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2); + }, + + // 创建弹跳效果 + /** + * @param {number} k + * @return {number} + */ + bounceIn: function (k) { + return 1 - easing.bounceOut(1 - k); + }, + /** + * @param {number} k + * @return {number} + */ + bounceOut: function (k) { + if (k < (1 / 2.75)) { + return 7.5625 * k * k; + } + else if (k < (2 / 2.75)) { + return 7.5625 * (k -= (1.5 / 2.75)) * k + 0.75; + } + else if (k < (2.5 / 2.75)) { + return 7.5625 * (k -= (2.25 / 2.75)) * k + 0.9375; + } + else { + return 7.5625 * (k -= (2.625 / 2.75)) * k + 0.984375; + } + }, + /** + * @param {number} k + * @return {number} + */ + bounceInOut: function (k) { + if (k < 0.5) { + return easing.bounceIn(k * 2) * 0.5; + } + return easing.bounceOut(k * 2 - 1) * 0.5 + 0.5; + } + }; + + module.exports = easing; + + + + +/***/ }, +/* 39 */ +/***/ function(module, exports) { + + /** + * @module zrender/tool/color + */ + + + var kCSSColorTable = { + 'transparent': [0,0,0,0], 'aliceblue': [240,248,255,1], + 'antiquewhite': [250,235,215,1], 'aqua': [0,255,255,1], + 'aquamarine': [127,255,212,1], 'azure': [240,255,255,1], + 'beige': [245,245,220,1], 'bisque': [255,228,196,1], + 'black': [0,0,0,1], 'blanchedalmond': [255,235,205,1], + 'blue': [0,0,255,1], 'blueviolet': [138,43,226,1], + 'brown': [165,42,42,1], 'burlywood': [222,184,135,1], + 'cadetblue': [95,158,160,1], 'chartreuse': [127,255,0,1], + 'chocolate': [210,105,30,1], 'coral': [255,127,80,1], + 'cornflowerblue': [100,149,237,1], 'cornsilk': [255,248,220,1], + 'crimson': [220,20,60,1], 'cyan': [0,255,255,1], + 'darkblue': [0,0,139,1], 'darkcyan': [0,139,139,1], + 'darkgoldenrod': [184,134,11,1], 'darkgray': [169,169,169,1], + 'darkgreen': [0,100,0,1], 'darkgrey': [169,169,169,1], + 'darkkhaki': [189,183,107,1], 'darkmagenta': [139,0,139,1], + 'darkolivegreen': [85,107,47,1], 'darkorange': [255,140,0,1], + 'darkorchid': [153,50,204,1], 'darkred': [139,0,0,1], + 'darksalmon': [233,150,122,1], 'darkseagreen': [143,188,143,1], + 'darkslateblue': [72,61,139,1], 'darkslategray': [47,79,79,1], + 'darkslategrey': [47,79,79,1], 'darkturquoise': [0,206,209,1], + 'darkviolet': [148,0,211,1], 'deeppink': [255,20,147,1], + 'deepskyblue': [0,191,255,1], 'dimgray': [105,105,105,1], + 'dimgrey': [105,105,105,1], 'dodgerblue': [30,144,255,1], + 'firebrick': [178,34,34,1], 'floralwhite': [255,250,240,1], + 'forestgreen': [34,139,34,1], 'fuchsia': [255,0,255,1], + 'gainsboro': [220,220,220,1], 'ghostwhite': [248,248,255,1], + 'gold': [255,215,0,1], 'goldenrod': [218,165,32,1], + 'gray': [128,128,128,1], 'green': [0,128,0,1], + 'greenyellow': [173,255,47,1], 'grey': [128,128,128,1], + 'honeydew': [240,255,240,1], 'hotpink': [255,105,180,1], + 'indianred': [205,92,92,1], 'indigo': [75,0,130,1], + 'ivory': [255,255,240,1], 'khaki': [240,230,140,1], + 'lavender': [230,230,250,1], 'lavenderblush': [255,240,245,1], + 'lawngreen': [124,252,0,1], 'lemonchiffon': [255,250,205,1], + 'lightblue': [173,216,230,1], 'lightcoral': [240,128,128,1], + 'lightcyan': [224,255,255,1], 'lightgoldenrodyellow': [250,250,210,1], + 'lightgray': [211,211,211,1], 'lightgreen': [144,238,144,1], + 'lightgrey': [211,211,211,1], 'lightpink': [255,182,193,1], + 'lightsalmon': [255,160,122,1], 'lightseagreen': [32,178,170,1], + 'lightskyblue': [135,206,250,1], 'lightslategray': [119,136,153,1], + 'lightslategrey': [119,136,153,1], 'lightsteelblue': [176,196,222,1], + 'lightyellow': [255,255,224,1], 'lime': [0,255,0,1], + 'limegreen': [50,205,50,1], 'linen': [250,240,230,1], + 'magenta': [255,0,255,1], 'maroon': [128,0,0,1], + 'mediumaquamarine': [102,205,170,1], 'mediumblue': [0,0,205,1], + 'mediumorchid': [186,85,211,1], 'mediumpurple': [147,112,219,1], + 'mediumseagreen': [60,179,113,1], 'mediumslateblue': [123,104,238,1], + 'mediumspringgreen': [0,250,154,1], 'mediumturquoise': [72,209,204,1], + 'mediumvioletred': [199,21,133,1], 'midnightblue': [25,25,112,1], + 'mintcream': [245,255,250,1], 'mistyrose': [255,228,225,1], + 'moccasin': [255,228,181,1], 'navajowhite': [255,222,173,1], + 'navy': [0,0,128,1], 'oldlace': [253,245,230,1], + 'olive': [128,128,0,1], 'olivedrab': [107,142,35,1], + 'orange': [255,165,0,1], 'orangered': [255,69,0,1], + 'orchid': [218,112,214,1], 'palegoldenrod': [238,232,170,1], + 'palegreen': [152,251,152,1], 'paleturquoise': [175,238,238,1], + 'palevioletred': [219,112,147,1], 'papayawhip': [255,239,213,1], + 'peachpuff': [255,218,185,1], 'peru': [205,133,63,1], + 'pink': [255,192,203,1], 'plum': [221,160,221,1], + 'powderblue': [176,224,230,1], 'purple': [128,0,128,1], + 'red': [255,0,0,1], 'rosybrown': [188,143,143,1], + 'royalblue': [65,105,225,1], 'saddlebrown': [139,69,19,1], + 'salmon': [250,128,114,1], 'sandybrown': [244,164,96,1], + 'seagreen': [46,139,87,1], 'seashell': [255,245,238,1], + 'sienna': [160,82,45,1], 'silver': [192,192,192,1], + 'skyblue': [135,206,235,1], 'slateblue': [106,90,205,1], + 'slategray': [112,128,144,1], 'slategrey': [112,128,144,1], + 'snow': [255,250,250,1], 'springgreen': [0,255,127,1], + 'steelblue': [70,130,180,1], 'tan': [210,180,140,1], + 'teal': [0,128,128,1], 'thistle': [216,191,216,1], + 'tomato': [255,99,71,1], 'turquoise': [64,224,208,1], + 'violet': [238,130,238,1], 'wheat': [245,222,179,1], + 'white': [255,255,255,1], 'whitesmoke': [245,245,245,1], + 'yellow': [255,255,0,1], 'yellowgreen': [154,205,50,1] + }; + + function clampCssByte(i) { // Clamp to integer 0 .. 255. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 255 ? 255 : i; + } + + function clampCssAngle(i) { // Clamp to integer 0 .. 360. + i = Math.round(i); // Seems to be what Chrome does (vs truncation). + return i < 0 ? 0 : i > 360 ? 360 : i; + } + + function clampCssFloat(f) { // Clamp to float 0.0 .. 1.0. + return f < 0 ? 0 : f > 1 ? 1 : f; + } + + function parseCssInt(str) { // int or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssByte(parseFloat(str) / 100 * 255); + } + return clampCssByte(parseInt(str, 10)); + } + + function parseCssFloat(str) { // float or percentage. + if (str.length && str.charAt(str.length - 1) === '%') { + return clampCssFloat(parseFloat(str) / 100); + } + return clampCssFloat(parseFloat(str)); + } + + function cssHueToRgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + else if (h > 1) { + h -= 1; + } + + if (h * 6 < 1) { + return m1 + (m2 - m1) * h * 6; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2/3 - h) * 6; + } + return m1; + } + + function lerp(a, b, p) { + return a + (b - a) * p; + } + + /** + * @param {string} colorStr + * @return {Array.} + * @memberOf module:zrender/util/color + */ + function parse(colorStr) { + if (!colorStr) { + return; + } + // colorStr may be not string + colorStr = colorStr + ''; + // Remove all whitespace, not compliant, but should just be more accepting. + var str = colorStr.replace(/ /g, '').toLowerCase(); + + // Color keywords (and transparent) lookup. + if (str in kCSSColorTable) { + return kCSSColorTable[str].slice(); // dup. + } + + // #abc and #abc123 syntax. + if (str.charAt(0) === '#') { + if (str.length === 4) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xfff)) { + return; // Covers NaN. + } + return [ + ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), + (iv & 0xf0) | ((iv & 0xf0) >> 4), + (iv & 0xf) | ((iv & 0xf) << 4), + 1 + ]; + } + else if (str.length === 7) { + var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing. + if (!(iv >= 0 && iv <= 0xffffff)) { + return; // Covers NaN. + } + return [ + (iv & 0xff0000) >> 16, + (iv & 0xff00) >> 8, + iv & 0xff, + 1 + ]; + } + + return; + } + var op = str.indexOf('('), ep = str.indexOf(')'); + if (op !== -1 && ep + 1 === str.length) { + var fname = str.substr(0, op); + var params = str.substr(op + 1, ep - (op + 1)).split(','); + var alpha = 1; // To allow case fallthrough. + switch (fname) { + case 'rgba': + if (params.length !== 4) { + return; + } + alpha = parseCssFloat(params.pop()); // jshint ignore:line + // Fall through. + case 'rgb': + if (params.length !== 3) { + return; + } + return [ + parseCssInt(params[0]), + parseCssInt(params[1]), + parseCssInt(params[2]), + alpha + ]; + case 'hsla': + if (params.length !== 4) { + return; + } + params[3] = parseCssFloat(params[3]); + return hsla2rgba(params); + case 'hsl': + if (params.length !== 3) { + return; + } + return hsla2rgba(params); + default: + return; + } + } + + return; + } + + /** + * @param {Array.} hsla + * @return {Array.} rgba + */ + function hsla2rgba(hsla) { + var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; // 0 .. 1 + // NOTE(deanm): According to the CSS spec s/l should only be + // percentages, but we don't bother and let float or percentage. + var s = parseCssFloat(hsla[1]); + var l = parseCssFloat(hsla[2]); + var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s; + var m1 = l * 2 - m2; + + var rgba = [ + clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255), + clampCssByte(cssHueToRgb(m1, m2, h) * 255), + clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255) + ]; + + if (hsla.length === 4) { + rgba[3] = hsla[3]; + } + + return rgba; + } + + /** + * @param {Array.} rgba + * @return {Array.} hsla + */ + function rgba2hsla(rgba) { + if (!rgba) { + return; + } + + // RGB from 0 to 255 + var R = rgba[0] / 255; + var G = rgba[1] / 255; + var B = rgba[2] / 255; + + var vMin = Math.min(R, G, B); // Min. value of RGB + var vMax = Math.max(R, G, B); // Max. value of RGB + var delta = vMax - vMin; // Delta RGB value + + var L = (vMax + vMin) / 2; + var H; + var S; + // HSL results from 0 to 1 + if (delta === 0) { + H = 0; + S = 0; + } + else { + if (L < 0.5) { + S = delta / (vMax + vMin); + } + else { + S = delta / (2 - vMax - vMin); + } + + var deltaR = (((vMax - R) / 6) + (delta / 2)) / delta; + var deltaG = (((vMax - G) / 6) + (delta / 2)) / delta; + var deltaB = (((vMax - B) / 6) + (delta / 2)) / delta; + + if (R === vMax) { + H = deltaB - deltaG; + } + else if (G === vMax) { + H = (1 / 3) + deltaR - deltaB; + } + else if (B === vMax) { + H = (2 / 3) + deltaG - deltaR; + } + + if (H < 0) { + H += 1; + } + + if (H > 1) { + H -= 1; + } + } + + var hsla = [H * 360, S, L]; + + if (rgba[3] != null) { + hsla.push(rgba[3]); + } + + return hsla; + } + + /** + * @param {string} color + * @param {number} level + * @return {string} + * @memberOf module:zrender/util/color + */ + function lift(color, level) { + var colorArr = parse(color); + if (colorArr) { + for (var i = 0; i < 3; i++) { + if (level < 0) { + colorArr[i] = colorArr[i] * (1 - level) | 0; + } + else { + colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0; + } + } + return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); + } + } + + /** + * @param {string} color + * @return {string} + * @memberOf module:zrender/util/color + */ + function toHex(color, level) { + var colorArr = parse(color); + if (colorArr) { + return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); + } + } + + /** + * Map value to color. Faster than mapToColor methods because color is represented by rgba array + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.>} colors List of rgba color array + * @param {Array.} [out] Mapped gba color array + * @return {Array.} + */ + function fastMapToColor(normalizedValue, colors, out) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + out = out || [0, 0, 0, 0]; + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = colors[leftIndex]; + var rightColor = colors[rightIndex]; + var dv = value - leftIndex; + out[0] = clampCssByte(lerp(leftColor[0], rightColor[0], dv)); + out[1] = clampCssByte(lerp(leftColor[1], rightColor[1], dv)); + out[2] = clampCssByte(lerp(leftColor[2], rightColor[2], dv)); + out[3] = clampCssByte(lerp(leftColor[3], rightColor[3], dv)); + return out; + } + /** + * @param {number} normalizedValue A float between 0 and 1. + * @param {Array.} colors Color list. + * @param {boolean=} fullOutput Default false. + * @return {(string|Object)} Result color. If fullOutput, + * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...}, + * @memberOf module:zrender/util/color + */ + function mapToColor(normalizedValue, colors, fullOutput) { + if (!(colors && colors.length) + || !(normalizedValue >= 0 && normalizedValue <= 1) + ) { + return; + } + + var value = normalizedValue * (colors.length - 1); + var leftIndex = Math.floor(value); + var rightIndex = Math.ceil(value); + var leftColor = parse(colors[leftIndex]); + var rightColor = parse(colors[rightIndex]); + var dv = value - leftIndex; + + var color = stringify( + [ + clampCssByte(lerp(leftColor[0], rightColor[0], dv)), + clampCssByte(lerp(leftColor[1], rightColor[1], dv)), + clampCssByte(lerp(leftColor[2], rightColor[2], dv)), + clampCssFloat(lerp(leftColor[3], rightColor[3], dv)) + ], + 'rgba' + ); + + return fullOutput + ? { + color: color, + leftIndex: leftIndex, + rightIndex: rightIndex, + value: value + } + : color; + } + + /** + * @param {string} color + * @param {number=} h 0 ~ 360, ignore when null. + * @param {number=} s 0 ~ 1, ignore when null. + * @param {number=} l 0 ~ 1, ignore when null. + * @return {string} Color string in rgba format. + * @memberOf module:zrender/util/color + */ + function modifyHSL(color, h, s, l) { + color = parse(color); + + if (color) { + color = rgba2hsla(color); + h != null && (color[0] = clampCssAngle(h)); + s != null && (color[1] = parseCssFloat(s)); + l != null && (color[2] = parseCssFloat(l)); + + return stringify(hsla2rgba(color), 'rgba'); + } + } + + /** + * @param {string} color + * @param {number=} alpha 0 ~ 1 + * @return {string} Color string in rgba format. + * @memberOf module:zrender/util/color + */ + function modifyAlpha(color, alpha) { + color = parse(color); + + if (color && alpha != null) { + color[3] = clampCssFloat(alpha); + return stringify(color, 'rgba'); + } + } + + /** + * @param {Array.} colors Color list. + * @param {string} type 'rgba', 'hsva', ... + * @return {string} Result color. + */ + function stringify(arrColor, type) { + var colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2]; + if (type === 'rgba' || type === 'hsva' || type === 'hsla') { + colorStr += ',' + arrColor[3]; + } + return type + '(' + colorStr + ')'; + } + + module.exports = { + parse: parse, + lift: lift, + toHex: toHex, + fastMapToColor: fastMapToColor, + mapToColor: mapToColor, + modifyHSL: modifyHSL, + modifyAlpha: modifyAlpha, + stringify: stringify + }; + + + + +/***/ }, +/* 40 */ +/***/ function(module, exports, __webpack_require__) { + + + var config = __webpack_require__(41); + + /** + * @exports zrender/tool/log + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + module.exports = function() { + if (config.debugMode === 0) { + return; + } + else if (config.debugMode == 1) { + for (var k in arguments) { + throw new Error(arguments[k]); + } + } + else if (config.debugMode > 1) { + for (var k in arguments) { + console.log(arguments[k]); + } + } + }; + + /* for debug + return function(mes) { + document.getElementById('wrong-message').innerHTML = + mes + ' ' + (new Date() - 0) + + '
' + + document.getElementById('wrong-message').innerHTML; + }; + */ + + + +/***/ }, +/* 41 */ +/***/ function(module, exports) { + + + var dpr = 1; + // If in browser environment + if (typeof window !== 'undefined') { + dpr = Math.max(window.devicePixelRatio || 1, 1); + } + /** + * config默认配置项 + * @exports zrender/config + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + var config = { + /** + * debug日志选项:catchBrushException为true下有效 + * 0 : 不生成debug数据,发布用 + * 1 : 异常抛出,调试用 + * 2 : 控制台输出,调试用 + */ + debugMode: 0, + + // retina 屏幕优化 + devicePixelRatio: dpr + }; + module.exports = config; + + + + +/***/ }, +/* 42 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Group = __webpack_require__(30); + var componentUtil = __webpack_require__(20); + var clazzUtil = __webpack_require__(13); + + function Chart() { + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new Group(); + + /** + * @type {string} + * @readOnly + */ + this.uid = componentUtil.getUID('viewChart'); + } + + Chart.prototype = { + + type: 'chart', + + /** + * Init the chart + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + init: function (ecModel, api) {}, + + /** + * Render the chart + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + render: function (seriesModel, ecModel, api, payload) {}, + + /** + * Highlight series or specified data item + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + highlight: function (seriesModel, ecModel, api, payload) { + toggleHighlight(seriesModel.getData(), payload, 'emphasis'); + }, + + /** + * Downplay series or specified data item + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @param {Object} payload + */ + downplay: function (seriesModel, ecModel, api, payload) { + toggleHighlight(seriesModel.getData(), payload, 'normal'); + }, + + /** + * Remove self + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + remove: function (ecModel, api) { + this.group.removeAll(); + }, + + /** + * Dispose self + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + dispose: function () {} + }; + + var chartProto = Chart.prototype; + chartProto.updateView + = chartProto.updateLayout + = chartProto.updateVisual + = function (seriesModel, ecModel, api, payload) { + this.render(seriesModel, ecModel, api, payload); + }; + + /** + * Set state of single element + * @param {module:zrender/Element} el + * @param {string} state + */ + function elSetState(el, state) { + if (el) { + el.trigger(state); + if (el.type === 'group') { + for (var i = 0; i < el.childCount(); i++) { + elSetState(el.childAt(i), state); + } + } + } + } + /** + * @param {module:echarts/data/List} data + * @param {Object} payload + * @param {string} state 'normal'|'emphasis' + * @inner + */ + function toggleHighlight(data, payload, state) { + var dataIndex = payload && payload.dataIndex; + var name = payload && payload.name; + + if (dataIndex != null) { + var dataIndices = dataIndex instanceof Array ? dataIndex : [dataIndex]; + for (var i = 0, len = dataIndices.length; i < len; i++) { + elSetState(data.getItemGraphicEl(dataIndices[i]), state); + } + } + else if (name) { + var names = name instanceof Array ? name : [name]; + for (var i = 0, len = names.length; i < len; i++) { + var dataIndex = data.indexOfName(names[i]); + elSetState(data.getItemGraphicEl(dataIndex), state); + } + } + else { + data.eachItemGraphicEl(function (el) { + elSetState(el, state); + }); + } + } + + // Enable Chart.extend. + clazzUtil.enableClassExtend(Chart); + + // Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on. + clazzUtil.enableClassManagement(Chart, {registerWhenExtend: true}); + + module.exports = Chart; + + +/***/ }, +/* 43 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var pathTool = __webpack_require__(44); + var round = Math.round; + var Path = __webpack_require__(45); + var colorTool = __webpack_require__(39); + var matrix = __webpack_require__(11); + var vector = __webpack_require__(10); + var Gradient = __webpack_require__(61); + + var graphic = {}; + + graphic.Group = __webpack_require__(30); + + graphic.Image = __webpack_require__(62); + + graphic.Text = __webpack_require__(64); + + graphic.Circle = __webpack_require__(65); + + graphic.Sector = __webpack_require__(66); + + graphic.Ring = __webpack_require__(67); + + graphic.Polygon = __webpack_require__(68); + + graphic.Polyline = __webpack_require__(72); + + graphic.Rect = __webpack_require__(73); + + graphic.Line = __webpack_require__(75); + + graphic.BezierCurve = __webpack_require__(76); + + graphic.Arc = __webpack_require__(77); + + graphic.CompoundPath = __webpack_require__(78); + + graphic.LinearGradient = __webpack_require__(79); + + graphic.RadialGradient = __webpack_require__(80); + + graphic.BoundingRect = __webpack_require__(9); + + /** + * Extend shape with parameters + */ + graphic.extendShape = function (opts) { + return Path.extend(opts); + }; + + /** + * Extend path + */ + graphic.extendPath = function (pathData, opts) { + return pathTool.extendFromString(pathData, opts); + }; + + /** + * Create a path element from path data string + * @param {string} pathData + * @param {Object} opts + * @param {module:zrender/core/BoundingRect} rect + * @param {string} [layout=cover] 'center' or 'cover' + */ + graphic.makePath = function (pathData, opts, rect, layout) { + var path = pathTool.createFromString(pathData, opts); + var boundingRect = path.getBoundingRect(); + if (rect) { + var aspect = boundingRect.width / boundingRect.height; + + if (layout === 'center') { + // Set rect to center, keep width / height ratio. + var width = rect.height * aspect; + var height; + if (width <= rect.width) { + height = rect.height; + } + else { + width = rect.width; + height = width / aspect; + } + var cx = rect.x + rect.width / 2; + var cy = rect.y + rect.height / 2; + + rect.x = cx - width / 2; + rect.y = cy - height / 2; + rect.width = width; + rect.height = height; + } + + this.resizePath(path, rect); + } + return path; + }; + + graphic.mergePath = pathTool.mergePath, + + /** + * Resize a path to fit the rect + * @param {module:zrender/graphic/Path} path + * @param {Object} rect + */ + graphic.resizePath = function (path, rect) { + if (!path.applyTransform) { + return; + } + + var pathRect = path.getBoundingRect(); + + var m = pathRect.calculateTransform(rect); + + path.applyTransform(m); + }; + + /** + * Sub pixel optimize line for canvas + * + * @param {Object} param + * @param {Object} [param.shape] + * @param {number} [param.shape.x1] + * @param {number} [param.shape.y1] + * @param {number} [param.shape.x2] + * @param {number} [param.shape.y2] + * @param {Object} [param.style] + * @param {number} [param.style.lineWidth] + * @return {Object} Modified param + */ + graphic.subPixelOptimizeLine = function (param) { + var subPixelOptimize = graphic.subPixelOptimize; + var shape = param.shape; + var lineWidth = param.style.lineWidth; + + if (round(shape.x1 * 2) === round(shape.x2 * 2)) { + shape.x1 = shape.x2 = subPixelOptimize(shape.x1, lineWidth, true); + } + if (round(shape.y1 * 2) === round(shape.y2 * 2)) { + shape.y1 = shape.y2 = subPixelOptimize(shape.y1, lineWidth, true); + } + return param; + }; + + /** + * Sub pixel optimize rect for canvas + * + * @param {Object} param + * @param {Object} [param.shape] + * @param {number} [param.shape.x] + * @param {number} [param.shape.y] + * @param {number} [param.shape.width] + * @param {number} [param.shape.height] + * @param {Object} [param.style] + * @param {number} [param.style.lineWidth] + * @return {Object} Modified param + */ + graphic.subPixelOptimizeRect = function (param) { + var subPixelOptimize = graphic.subPixelOptimize; + var shape = param.shape; + var lineWidth = param.style.lineWidth; + var originX = shape.x; + var originY = shape.y; + var originWidth = shape.width; + var originHeight = shape.height; + shape.x = subPixelOptimize(shape.x, lineWidth, true); + shape.y = subPixelOptimize(shape.y, lineWidth, true); + shape.width = Math.max( + subPixelOptimize(originX + originWidth, lineWidth, false) - shape.x, + originWidth === 0 ? 0 : 1 + ); + shape.height = Math.max( + subPixelOptimize(originY + originHeight, lineWidth, false) - shape.y, + originHeight === 0 ? 0 : 1 + ); + return param; + }; + + /** + * Sub pixel optimize for canvas + * + * @param {number} position Coordinate, such as x, y + * @param {number} lineWidth Should be nonnegative integer. + * @param {boolean=} positiveOrNegative Default false (negative). + * @return {number} Optimized position. + */ + graphic.subPixelOptimize = function (position, lineWidth, positiveOrNegative) { + // Assure that (position + lineWidth / 2) is near integer edge, + // otherwise line will be fuzzy in canvas. + var doubledPosition = round(position * 2); + return (doubledPosition + round(lineWidth)) % 2 === 0 + ? doubledPosition / 2 + : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; + }; + + function hasFillOrStroke(fillOrStroke) { + return fillOrStroke != null && fillOrStroke != 'none'; + } + + function liftColor(color) { + return typeof color === 'string' ? colorTool.lift(color, -0.1) : color; + } + + /** + * @private + */ + function cacheElementStl(el) { + if (el.__hoverStlDirty) { + var stroke = el.style.stroke; + var fill = el.style.fill; + + // Create hoverStyle on mouseover + var hoverStyle = el.__hoverStl; + hoverStyle.fill = hoverStyle.fill + || (hasFillOrStroke(fill) ? liftColor(fill) : null); + hoverStyle.stroke = hoverStyle.stroke + || (hasFillOrStroke(stroke) ? liftColor(stroke) : null); + + var normalStyle = {}; + for (var name in hoverStyle) { + if (hoverStyle.hasOwnProperty(name)) { + normalStyle[name] = el.style[name]; + } + } + + el.__normalStl = normalStyle; + + el.__hoverStlDirty = false; + } + } + + /** + * @private + */ + function doSingleEnterHover(el) { + if (el.__isHover) { + return; + } + + cacheElementStl(el); + + if (el.useHoverLayer) { + el.__zr && el.__zr.addHover(el, el.__hoverStl); + } + else { + el.setStyle(el.__hoverStl); + el.z2 += 1; + } + + el.__isHover = true; + } + + /** + * @inner + */ + function doSingleLeaveHover(el) { + if (!el.__isHover) { + return; + } + + var normalStl = el.__normalStl; + if (el.useHoverLayer) { + el.__zr && el.__zr.removeHover(el); + } + else { + normalStl && el.setStyle(normalStl); + el.z2 -= 1; + } + + el.__isHover = false; + } + + /** + * @inner + */ + function doEnterHover(el) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + doSingleEnterHover(child); + } + }) + : doSingleEnterHover(el); + } + + function doLeaveHover(el) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + doSingleLeaveHover(child); + } + }) + : doSingleLeaveHover(el); + } + + /** + * @inner + */ + function setElementHoverStl(el, hoverStl) { + // If element has sepcified hoverStyle, then use it instead of given hoverStyle + // Often used when item group has a label element and it's hoverStyle is different + el.__hoverStl = el.hoverStyle || hoverStl || {}; + el.__hoverStlDirty = true; + + if (el.__isHover) { + cacheElementStl(el); + } + } + + /** + * @inner + */ + function onElementMouseOver() { + // Only if element is not in emphasis status + !this.__isEmphasis && doEnterHover(this); + } + + /** + * @inner + */ + function onElementMouseOut() { + // Only if element is not in emphasis status + !this.__isEmphasis && doLeaveHover(this); + } + + /** + * @inner + */ + function enterEmphasis() { + this.__isEmphasis = true; + doEnterHover(this); + } + + /** + * @inner + */ + function leaveEmphasis() { + this.__isEmphasis = false; + doLeaveHover(this); + } + + /** + * Set hover style of element + * @param {module:zrender/Element} el + * @param {Object} [hoverStyle] + */ + graphic.setHoverStyle = function (el, hoverStyle) { + el.type === 'group' + ? el.traverse(function (child) { + if (child.type !== 'group') { + setElementHoverStl(child, hoverStyle); + } + }) + : setElementHoverStl(el, hoverStyle); + // Remove previous bound handlers + el.on('mouseover', onElementMouseOver) + .on('mouseout', onElementMouseOut); + + // Emphasis, normal can be triggered manually + el.on('emphasis', enterEmphasis) + .on('normal', leaveEmphasis); + }; + + /** + * Set text option in the style + * @param {Object} textStyle + * @param {module:echarts/model/Model} labelModel + * @param {string} color + */ + graphic.setText = function (textStyle, labelModel, color) { + var labelPosition = labelModel.getShallow('position') || 'inside'; + var labelColor = labelPosition.indexOf('inside') >= 0 ? 'white' : color; + var textStyleModel = labelModel.getModel('textStyle'); + zrUtil.extend(textStyle, { + textDistance: labelModel.getShallow('distance') || 5, + textFont: textStyleModel.getFont(), + textPosition: labelPosition, + textFill: textStyleModel.getTextColor() || labelColor + }); + }; + + function animateOrSetProps(isUpdate, el, props, animatableModel, dataIndex, cb) { + if (typeof dataIndex === 'function') { + cb = dataIndex; + dataIndex = null; + } + var animationEnabled = animatableModel + && ( + animatableModel.ifEnableAnimation + ? animatableModel.ifEnableAnimation() + // Directly use animation property + : animatableModel.getShallow('animation') + ); + + if (animationEnabled) { + var postfix = isUpdate ? 'Update' : ''; + var duration = animatableModel + && animatableModel.getShallow('animationDuration' + postfix); + var animationEasing = animatableModel + && animatableModel.getShallow('animationEasing' + postfix); + var animationDelay = animatableModel + && animatableModel.getShallow('animationDelay' + postfix); + if (typeof animationDelay === 'function') { + animationDelay = animationDelay(dataIndex); + } + duration > 0 + ? el.animateTo(props, duration, animationDelay || 0, animationEasing, cb) + : (el.attr(props), cb && cb()); + } + else { + el.attr(props); + cb && cb(); + } + } + /** + * Update graphic element properties with or without animation according to the configuration in series + * @param {module:zrender/Element} el + * @param {Object} props + * @param {module:echarts/model/Model} [animatableModel] + * @param {number} [dataIndex] + * @param {Function} [cb] + * @example + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); + * // Or + * graphic.updateProps(el, { + * position: [100, 100] + * }, seriesModel, function () { console.log('Animation done!'); }); + */ + graphic.updateProps = function (el, props, animatableModel, dataIndex, cb) { + animateOrSetProps(true, el, props, animatableModel, dataIndex, cb); + }; + + /** + * Init graphic element properties with or without animation according to the configuration in series + * @param {module:zrender/Element} el + * @param {Object} props + * @param {module:echarts/model/Model} [animatableModel] + * @param {number} [dataIndex] + * @param {Function} cb + */ + graphic.initProps = function (el, props, animatableModel, dataIndex, cb) { + animateOrSetProps(false, el, props, animatableModel, dataIndex, cb); + }; + + /** + * Get transform matrix of target (param target), + * in coordinate of its ancestor (param ancestor) + * + * @param {module:zrender/mixin/Transformable} target + * @param {module:zrender/mixin/Transformable} [ancestor] + */ + graphic.getTransform = function (target, ancestor) { + var mat = matrix.identity([]); + + while (target && target !== ancestor) { + matrix.mul(mat, target.getLocalTransform(), mat); + target = target.parent; + } + + return mat; + }; + + /** + * Apply transform to an vertex. + * @param {Array.} vertex [x, y] + * @param {Array.} transform Transform matrix: like [1, 0, 0, 1, 0, 0] + * @param {boolean=} invert Whether use invert matrix. + * @return {Array.} [x, y] + */ + graphic.applyTransform = function (vertex, transform, invert) { + if (invert) { + transform = matrix.invert([], transform); + } + return vector.applyTransform([], vertex, transform); + }; + + /** + * @param {string} direction 'left' 'right' 'top' 'bottom' + * @param {Array.} transform Transform matrix: like [1, 0, 0, 1, 0, 0] + * @param {boolean=} invert Whether use invert matrix. + * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom' + */ + graphic.transformDirection = function (direction, transform, invert) { + + // Pick a base, ensure that transform result will not be (0, 0). + var hBase = (transform[4] === 0 || transform[5] === 0 || transform[0] === 0) + ? 1 : Math.abs(2 * transform[4] / transform[0]); + var vBase = (transform[4] === 0 || transform[5] === 0 || transform[2] === 0) + ? 1 : Math.abs(2 * transform[4] / transform[2]); + + var vertex = [ + direction === 'left' ? -hBase : direction === 'right' ? hBase : 0, + direction === 'top' ? -vBase : direction === 'bottom' ? vBase : 0 + ]; + + vertex = graphic.applyTransform(vertex, transform, invert); + + return Math.abs(vertex[0]) > Math.abs(vertex[1]) + ? (vertex[0] > 0 ? 'right' : 'left') + : (vertex[1] > 0 ? 'bottom' : 'top'); + }; + + /** + * Apply group transition animation from g1 to g2 + */ + graphic.groupTransition = function (g1, g2, animatableModel, cb) { + if (!g1 || !g2) { + return; + } + + function getElMap(g) { + var elMap = {}; + g.traverse(function (el) { + if (!el.isGroup && el.anid) { + elMap[el.anid] = el; + } + }); + return elMap; + } + function getAnimatableProps(el) { + var obj = { + position: vector.clone(el.position), + rotation: el.rotation + }; + if (el.shape) { + obj.shape = zrUtil.extend({}, el.shape); + } + return obj; + } + var elMap1 = getElMap(g1); + + g2.traverse(function (el) { + if (!el.isGroup && el.anid) { + var oldEl = elMap1[el.anid]; + if (oldEl) { + var newProp = getAnimatableProps(el); + el.attr(getAnimatableProps(oldEl)); + graphic.updateProps(el, newProp, animatableModel, el.dataIndex); + } + // else { + // if (el.previousProps) { + // graphic.updateProps + // } + // } + } + }); + }; + + module.exports = graphic; + + +/***/ }, +/* 44 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Path = __webpack_require__(45); + var PathProxy = __webpack_require__(49); + var transformPath = __webpack_require__(60); + var matrix = __webpack_require__(11); + + // command chars + var cc = [ + 'm', 'M', 'l', 'L', 'v', 'V', 'h', 'H', 'z', 'Z', + 'c', 'C', 'q', 'Q', 't', 'T', 's', 'S', 'a', 'A' + ]; + + var mathSqrt = Math.sqrt; + var mathSin = Math.sin; + var mathCos = Math.cos; + var PI = Math.PI; + + var vMag = function(v) { + return Math.sqrt(v[0] * v[0] + v[1] * v[1]); + }; + var vRatio = function(u, v) { + return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); + }; + var vAngle = function(u, v) { + return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) + * Math.acos(vRatio(u, v)); + }; + + function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) { + var psi = psiDeg * (PI / 180.0); + var xp = mathCos(psi) * (x1 - x2) / 2.0 + + mathSin(psi) * (y1 - y2) / 2.0; + var yp = -1 * mathSin(psi) * (x1 - x2) / 2.0 + + mathCos(psi) * (y1 - y2) / 2.0; + + var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry); + + if (lambda > 1) { + rx *= mathSqrt(lambda); + ry *= mathSqrt(lambda); + } + + var f = (fa === fs ? -1 : 1) + * mathSqrt((((rx * rx) * (ry * ry)) + - ((rx * rx) * (yp * yp)) + - ((ry * ry) * (xp * xp))) / ((rx * rx) * (yp * yp) + + (ry * ry) * (xp * xp)) + ) || 0; + + var cxp = f * rx * yp / ry; + var cyp = f * -ry * xp / rx; + + var cx = (x1 + x2) / 2.0 + + mathCos(psi) * cxp + - mathSin(psi) * cyp; + var cy = (y1 + y2) / 2.0 + + mathSin(psi) * cxp + + mathCos(psi) * cyp; + + var theta = vAngle([ 1, 0 ], [ (xp - cxp) / rx, (yp - cyp) / ry ]); + var u = [ (xp - cxp) / rx, (yp - cyp) / ry ]; + var v = [ (-1 * xp - cxp) / rx, (-1 * yp - cyp) / ry ]; + var dTheta = vAngle(u, v); + + if (vRatio(u, v) <= -1) { + dTheta = PI; + } + if (vRatio(u, v) >= 1) { + dTheta = 0; + } + if (fs === 0 && dTheta > 0) { + dTheta = dTheta - 2 * PI; + } + if (fs === 1 && dTheta < 0) { + dTheta = dTheta + 2 * PI; + } + + path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs); + } + + function createPathProxyFromString(data) { + if (!data) { + return []; + } + + // command string + var cs = data.replace(/-/g, ' -') + .replace(/ /g, ' ') + .replace(/ /g, ',') + .replace(/,,/g, ','); + + var n; + // create pipes so that we can split the data + for (n = 0; n < cc.length; n++) { + cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]); + } + + // create array + var arr = cs.split('|'); + // init context point + var cpx = 0; + var cpy = 0; + + var path = new PathProxy(); + var CMD = PathProxy.CMD; + + var prevCmd; + for (n = 1; n < arr.length; n++) { + var str = arr[n]; + var c = str.charAt(0); + var off = 0; + var p = str.slice(1).replace(/e,-/g, 'e-').split(','); + var cmd; + + if (p.length > 0 && p[0] === '') { + p.shift(); + } + + for (var i = 0; i < p.length; i++) { + p[i] = parseFloat(p[i]); + } + while (off < p.length && !isNaN(p[off])) { + if (isNaN(p[0])) { + break; + } + var ctlPtx; + var ctlPty; + + var rx; + var ry; + var psi; + var fa; + var fs; + + var x1 = cpx; + var y1 = cpy; + + // convert l, H, h, V, and v to L + switch (c) { + case 'l': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'L': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'm': + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + c = 'l'; + break; + case 'M': + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.M; + path.addData(cmd, cpx, cpy); + c = 'L'; + break; + case 'h': + cpx += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'H': + cpx = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'v': + cpy += p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'V': + cpy = p[off++]; + cmd = CMD.L; + path.addData(cmd, cpx, cpy); + break; + case 'C': + cmd = CMD.C; + path.addData( + cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++] + ); + cpx = p[off - 2]; + cpy = p[off - 1]; + break; + case 'c': + cmd = CMD.C; + path.addData( + cmd, + p[off++] + cpx, p[off++] + cpy, + p[off++] + cpx, p[off++] + cpy, + p[off++] + cpx, p[off++] + cpy + ); + cpx += p[off - 2]; + cpy += p[off - 1]; + break; + case 'S': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 's': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.C) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cmd = CMD.C; + x1 = cpx + p[off++]; + y1 = cpy + p[off++]; + cpx += p[off++]; + cpy += p[off++]; + path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); + break; + case 'Q': + x1 = p[off++]; + y1 = p[off++]; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'q': + x1 = p[off++] + cpx; + y1 = p[off++] + cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, x1, y1, cpx, cpy); + break; + case 'T': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 't': + ctlPtx = cpx; + ctlPty = cpy; + var len = path.len(); + var pathData = path.data; + if (prevCmd === CMD.Q) { + ctlPtx += cpx - pathData[len - 4]; + ctlPty += cpy - pathData[len - 3]; + } + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.Q; + path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); + break; + case 'A': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + + x1 = cpx, y1 = cpy; + cpx = p[off++]; + cpy = p[off++]; + cmd = CMD.A; + processArc( + x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path + ); + break; + case 'a': + rx = p[off++]; + ry = p[off++]; + psi = p[off++]; + fa = p[off++]; + fs = p[off++]; + + x1 = cpx, y1 = cpy; + cpx += p[off++]; + cpy += p[off++]; + cmd = CMD.A; + processArc( + x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path + ); + break; + } + } + + if (c === 'z' || c === 'Z') { + cmd = CMD.Z; + path.addData(cmd); + } + + prevCmd = cmd; + } + + path.toStatic(); + + return path; + } + + // TODO Optimize double memory cost problem + function createPathOptions(str, opts) { + var pathProxy = createPathProxyFromString(str); + var transform; + opts = opts || {}; + opts.buildPath = function (path) { + path.setData(pathProxy.data); + transform && transformPath(path, transform); + // Svg and vml renderer don't have context + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx); + } + }; + + opts.applyTransform = function (m) { + if (!transform) { + transform = matrix.create(); + } + matrix.mul(transform, m, transform); + this.dirty(true); + }; + + return opts; + } + + module.exports = { + /** + * Create a Path object from path string data + * http://www.w3.org/TR/SVG/paths.html#PathData + * @param {Object} opts Other options + */ + createFromString: function (str, opts) { + return new Path(createPathOptions(str, opts)); + }, + + /** + * Create a Path class from path string data + * @param {string} str + * @param {Object} opts Other options + */ + extendFromString: function (str, opts) { + return Path.extend(createPathOptions(str, opts)); + }, + + /** + * Merge multiple paths + */ + // TODO Apply transform + // TODO stroke dash + // TODO Optimize double memory cost problem + mergePath: function (pathEls, opts) { + var pathList = []; + var len = pathEls.length; + for (var i = 0; i < len; i++) { + var pathEl = pathEls[i]; + if (pathEl.__dirty) { + pathEl.buildPath(pathEl.path, pathEl.shape, true); + } + pathList.push(pathEl.path); + } + + var pathBundle = new Path(opts); + pathBundle.buildPath = function (path) { + path.appendPath(pathList); + // Svg and vml renderer don't have context + var ctx = path.getContext(); + if (ctx) { + path.rebuildPath(ctx); + } + }; + + return pathBundle; + } + }; + + +/***/ }, +/* 45 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Path element + * @module zrender/graphic/Path + */ + + + + var Displayable = __webpack_require__(46); + var zrUtil = __webpack_require__(4); + var PathProxy = __webpack_require__(49); + var pathContain = __webpack_require__(52); + + var Pattern = __webpack_require__(59); + var getCanvasPattern = Pattern.prototype.getCanvasPattern; + + var abs = Math.abs; + + /** + * @alias module:zrender/graphic/Path + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + function Path(opts) { + Displayable.call(this, opts); + + /** + * @type {module:zrender/core/PathProxy} + * @readOnly + */ + this.path = new PathProxy(); + } + + Path.prototype = { + + constructor: Path, + + type: 'path', + + __dirtyPath: true, + + strokeContainThreshold: 5, + + brush: function (ctx, prevEl) { + var style = this.style; + var path = this.path; + var hasStroke = style.hasStroke(); + var hasFill = style.hasFill(); + var fill = style.fill; + var stroke = style.stroke; + var hasFillGradient = hasFill && !!(fill.colorStops); + var hasStrokeGradient = hasStroke && !!(stroke.colorStops); + var hasFillPattern = hasFill && !!(fill.image); + var hasStrokePattern = hasStroke && !!(stroke.image); + + style.bind(ctx, this, prevEl); + this.setTransform(ctx); + + if (this.__dirty) { + var rect = this.getBoundingRect(); + // Update gradient because bounding rect may changed + if (hasFillGradient) { + this._fillGradient = style.getGradient(ctx, fill, rect); + } + if (hasStrokeGradient) { + this._strokeGradient = style.getGradient(ctx, stroke, rect); + } + } + // Use the gradient or pattern + if (hasFillGradient) { + // PENDING If may have affect the state + ctx.fillStyle = this._fillGradient; + } + else if (hasFillPattern) { + ctx.fillStyle = getCanvasPattern.call(fill, ctx); + } + if (hasStrokeGradient) { + ctx.strokeStyle = this._strokeGradient; + } + else if (hasStrokePattern) { + ctx.strokeStyle = getCanvasPattern.call(stroke, ctx); + } + + var lineDash = style.lineDash; + var lineDashOffset = style.lineDashOffset; + + var ctxLineDash = !!ctx.setLineDash; + + // Update path sx, sy + var scale = this.getGlobalScale(); + path.setScale(scale[0], scale[1]); + + // Proxy context + // Rebuild path in following 2 cases + // 1. Path is dirty + // 2. Path needs javascript implemented lineDash stroking. + // In this case, lineDash information will not be saved in PathProxy + if (this.__dirtyPath || ( + lineDash && !ctxLineDash && hasStroke + )) { + path = this.path.beginPath(ctx); + + // Setting line dash before build path + if (lineDash && !ctxLineDash) { + path.setLineDash(lineDash); + path.setLineDashOffset(lineDashOffset); + } + + this.buildPath(path, this.shape, false); + + // Clear path dirty flag + this.__dirtyPath = false; + } + else { + // Replay path building + ctx.beginPath(); + this.path.rebuildPath(ctx); + } + + hasFill && path.fill(ctx); + + if (lineDash && ctxLineDash) { + ctx.setLineDash(lineDash); + ctx.lineDashOffset = lineDashOffset; + } + + hasStroke && path.stroke(ctx); + + if (lineDash && ctxLineDash) { + // PENDING + // Remove lineDash + ctx.setLineDash([]); + } + + + this.restoreTransform(ctx); + + // Draw rect text + if (style.text || style.text === 0) { + // var rect = this.getBoundingRect(); + this.drawRectText(ctx, this.getBoundingRect()); + } + }, + + // When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath + // Like in circle + buildPath: function (ctx, shapeCfg, inBundle) {}, + + getBoundingRect: function () { + var rect = this._rect; + var style = this.style; + var needsUpdateRect = !rect; + if (needsUpdateRect) { + var path = this.path; + if (this.__dirtyPath) { + path.beginPath(); + this.buildPath(path, this.shape, false); + } + rect = path.getBoundingRect(); + } + this._rect = rect; + + if (style.hasStroke()) { + // Needs update rect with stroke lineWidth when + // 1. Element changes scale or lineWidth + // 2. Shape is changed + var rectWithStroke = this._rectWithStroke || (this._rectWithStroke = rect.clone()); + if (this.__dirty || needsUpdateRect) { + rectWithStroke.copy(rect); + // FIXME Must after updateTransform + var w = style.lineWidth; + // PENDING, Min line width is needed when line is horizontal or vertical + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + + // Only add extra hover lineWidth when there are no fill + if (!style.hasFill()) { + w = Math.max(w, this.strokeContainThreshold || 4); + } + // Consider line width + // Line scale can't be 0; + if (lineScale > 1e-10) { + rectWithStroke.width += w / lineScale; + rectWithStroke.height += w / lineScale; + rectWithStroke.x -= w / lineScale / 2; + rectWithStroke.y -= w / lineScale / 2; + } + } + + // Return rect with stroke + return rectWithStroke; + } + + return rect; + }, + + contain: function (x, y) { + var localPos = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + var style = this.style; + x = localPos[0]; + y = localPos[1]; + + if (rect.contain(x, y)) { + var pathData = this.path.data; + if (style.hasStroke()) { + var lineWidth = style.lineWidth; + var lineScale = style.strokeNoScale ? this.getLineScale() : 1; + // Line scale can't be 0; + if (lineScale > 1e-10) { + // Only add extra hover lineWidth when there are no fill + if (!style.hasFill()) { + lineWidth = Math.max(lineWidth, this.strokeContainThreshold); + } + if (pathContain.containStroke( + pathData, lineWidth / lineScale, x, y + )) { + return true; + } + } + } + if (style.hasFill()) { + return pathContain.contain(pathData, x, y); + } + } + return false; + }, + + /** + * @param {boolean} dirtyPath + */ + dirty: function (dirtyPath) { + if (dirtyPath == null) { + dirtyPath = true; + } + // Only mark dirty, not mark clean + if (dirtyPath) { + this.__dirtyPath = dirtyPath; + this._rect = null; + } + + this.__dirty = true; + + this.__zr && this.__zr.refresh(); + + // Used as a clipping path + if (this.__clipTarget) { + this.__clipTarget.dirty(); + } + }, + + /** + * Alias for animate('shape') + * @param {boolean} loop + */ + animateShape: function (loop) { + return this.animate('shape', loop); + }, + + // Overwrite attrKV + attrKV: function (key, value) { + // FIXME + if (key === 'shape') { + this.setShape(value); + this.__dirtyPath = true; + this._rect = null; + } + else { + Displayable.prototype.attrKV.call(this, key, value); + } + }, + + /** + * @param {Object|string} key + * @param {*} value + */ + setShape: function (key, value) { + var shape = this.shape; + // Path from string may not have shape + if (shape) { + if (zrUtil.isObject(key)) { + for (var name in key) { + shape[name] = key[name]; + } + } + else { + shape[key] = value; + } + this.dirty(true); + } + return this; + }, + + getLineScale: function () { + var m = this.transform; + // Get the line scale. + // Determinant of `m` means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + return m && abs(m[0] - 1) > 1e-10 && abs(m[3] - 1) > 1e-10 + ? Math.sqrt(abs(m[0] * m[3] - m[2] * m[1])) + : 1; + } + }; + + /** + * 扩展一个 Path element, 比如星形,圆等。 + * Extend a path element + * @param {Object} props + * @param {string} props.type Path type + * @param {Function} props.init Initialize + * @param {Function} props.buildPath Overwrite buildPath method + * @param {Object} [props.style] Extended default style config + * @param {Object} [props.shape] Extended default shape config + */ + Path.extend = function (defaults) { + var Sub = function (opts) { + Path.call(this, opts); + + if (defaults.style) { + // Extend default style + this.style.extendFrom(defaults.style, false); + } + + // Extend default shape + var defaultShape = defaults.shape; + if (defaultShape) { + this.shape = this.shape || {}; + var thisShape = this.shape; + for (var name in defaultShape) { + if ( + ! thisShape.hasOwnProperty(name) + && defaultShape.hasOwnProperty(name) + ) { + thisShape[name] = defaultShape[name]; + } + } + } + + defaults.init && defaults.init.call(this, opts); + }; + + zrUtil.inherits(Sub, Path); + + // FIXME 不能 extend position, rotation 等引用对象 + for (var name in defaults) { + // Extending prototype values and methods + if (name !== 'style' && name !== 'shape') { + Sub.prototype[name] = defaults[name]; + } + } + + return Sub; + }; + + zrUtil.inherits(Path, Displayable); + + module.exports = Path; + + +/***/ }, +/* 46 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 可绘制的图形基类 + * Base class of all displayable graphic objects + * @module zrender/graphic/Displayable + */ + + + + var zrUtil = __webpack_require__(4); + + var Style = __webpack_require__(47); + + var Element = __webpack_require__(31); + var RectText = __webpack_require__(48); + // var Stateful = require('./mixin/Stateful'); + + /** + * @alias module:zrender/graphic/Displayable + * @extends module:zrender/Element + * @extends module:zrender/graphic/mixin/RectText + */ + function Displayable(opts) { + + opts = opts || {}; + + Element.call(this, opts); + + // Extend properties + for (var name in opts) { + if ( + opts.hasOwnProperty(name) && + name !== 'style' + ) { + this[name] = opts[name]; + } + } + + /** + * @type {module:zrender/graphic/Style} + */ + this.style = new Style(opts.style); + + this._rect = null; + // Shapes for cascade clipping. + this.__clipPaths = []; + + // FIXME Stateful must be mixined after style is setted + // Stateful.call(this, opts); + } + + Displayable.prototype = { + + constructor: Displayable, + + type: 'displayable', + + /** + * Displayable 是否为脏,Painter 中会根据该标记判断是否需要是否需要重新绘制 + * Dirty flag. From which painter will determine if this displayable object needs brush + * @name module:zrender/graphic/Displayable#__dirty + * @type {boolean} + */ + __dirty: true, + + /** + * 图形是否可见,为true时不绘制图形,但是仍能触发鼠标事件 + * If ignore drawing of the displayable object. Mouse event will still be triggered + * @name module:/zrender/graphic/Displayable#invisible + * @type {boolean} + * @default false + */ + invisible: false, + + /** + * @name module:/zrender/graphic/Displayable#z + * @type {number} + * @default 0 + */ + z: 0, + + /** + * @name module:/zrender/graphic/Displayable#z + * @type {number} + * @default 0 + */ + z2: 0, + + /** + * z层level,决定绘画在哪层canvas中 + * @name module:/zrender/graphic/Displayable#zlevel + * @type {number} + * @default 0 + */ + zlevel: 0, + + /** + * 是否可拖拽 + * @name module:/zrender/graphic/Displayable#draggable + * @type {boolean} + * @default false + */ + draggable: false, + + /** + * 是否正在拖拽 + * @name module:/zrender/graphic/Displayable#draggable + * @type {boolean} + * @default false + */ + dragging: false, + + /** + * 是否相应鼠标事件 + * @name module:/zrender/graphic/Displayable#silent + * @type {boolean} + * @default false + */ + silent: false, + + /** + * If enable culling + * @type {boolean} + * @default false + */ + culling: false, + + /** + * Mouse cursor when hovered + * @name module:/zrender/graphic/Displayable#cursor + * @type {string} + */ + cursor: 'pointer', + + /** + * If hover area is bounding rect + * @name module:/zrender/graphic/Displayable#rectHover + * @type {string} + */ + rectHover: false, + + /** + * Render the element progressively when the value >= 0, + * usefull for large data. + * @type {number} + */ + progressive: -1, + + beforeBrush: function (ctx) {}, + + afterBrush: function (ctx) {}, + + /** + * 图形绘制方法 + * @param {Canvas2DRenderingContext} ctx + */ + // Interface + brush: function (ctx, prevEl) {}, + + /** + * 获取最小包围盒 + * @return {module:zrender/core/BoundingRect} + */ + // Interface + getBoundingRect: function () {}, + + /** + * 判断坐标 x, y 是否在图形上 + * If displayable element contain coord x, y + * @param {number} x + * @param {number} y + * @return {boolean} + */ + contain: function (x, y) { + return this.rectContain(x, y); + }, + + /** + * @param {Function} cb + * @param {} context + */ + traverse: function (cb, context) { + cb.call(context, this); + }, + + /** + * 判断坐标 x, y 是否在图形的包围盒上 + * If bounding rect of element contain coord x, y + * @param {number} x + * @param {number} y + * @return {boolean} + */ + rectContain: function (x, y) { + var coord = this.transformCoordToLocal(x, y); + var rect = this.getBoundingRect(); + return rect.contain(coord[0], coord[1]); + }, + + /** + * 标记图形元素为脏,并且在下一帧重绘 + * Mark displayable element dirty and refresh next frame + */ + dirty: function () { + this.__dirty = true; + + this._rect = null; + + this.__zr && this.__zr.refresh(); + }, + + /** + * 图形是否会触发事件 + * If displayable object binded any event + * @return {boolean} + */ + // TODO, 通过 bind 绑定的事件 + // isSilent: function () { + // return !( + // this.hoverable || this.draggable + // || this.onmousemove || this.onmouseover || this.onmouseout + // || this.onmousedown || this.onmouseup || this.onclick + // || this.ondragenter || this.ondragover || this.ondragleave + // || this.ondrop + // ); + // }, + /** + * Alias for animate('style') + * @param {boolean} loop + */ + animateStyle: function (loop) { + return this.animate('style', loop); + }, + + attrKV: function (key, value) { + if (key !== 'style') { + Element.prototype.attrKV.call(this, key, value); + } + else { + this.style.set(value); + } + }, + + /** + * @param {Object|string} key + * @param {*} value + */ + setStyle: function (key, value) { + this.style.set(key, value); + this.dirty(false); + return this; + }, + + /** + * Use given style object + * @param {Object} obj + */ + useStyle: function (obj) { + this.style = new Style(obj); + this.dirty(false); + return this; + } + }; + + zrUtil.inherits(Displayable, Element); + + zrUtil.mixin(Displayable, RectText); + // zrUtil.mixin(Displayable, Stateful); + + module.exports = Displayable; + + +/***/ }, +/* 47 */ +/***/ function(module, exports) { + + /** + * @module zrender/graphic/Style + */ + + + var STYLE_COMMON_PROPS = [ + ['shadowBlur', 0], ['shadowOffsetX', 0], ['shadowOffsetY', 0], ['shadowColor', '#000'], + ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10] + ]; + + // var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4); + // var LINE_PROPS = STYLE_COMMON_PROPS.slice(4); + + var Style = function (opts) { + this.extendFrom(opts); + }; + + function createLinearGradient(ctx, obj, rect) { + // var size = + var x = obj.x; + var x2 = obj.x2; + var y = obj.y; + var y2 = obj.y2; + + if (!obj.global) { + x = x * rect.width + rect.x; + x2 = x2 * rect.width + rect.x; + y = y * rect.height + rect.y; + y2 = y2 * rect.height + rect.y; + } + + var canvasGradient = ctx.createLinearGradient(x, y, x2, y2); + + return canvasGradient; + } + + function createRadialGradient(ctx, obj, rect) { + var width = rect.width; + var height = rect.height; + var min = Math.min(width, height); + + var x = obj.x; + var y = obj.y; + var r = obj.r; + if (!obj.global) { + x = x * width + rect.x; + y = y * height + rect.y; + r = r * min; + } + + var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); + + return canvasGradient; + } + + + Style.prototype = { + + constructor: Style, + + /** + * @type {string} + */ + fill: '#000000', + + /** + * @type {string} + */ + stroke: null, + + /** + * @type {number} + */ + opacity: 1, + + /** + * @type {Array.} + */ + lineDash: null, + + /** + * @type {number} + */ + lineDashOffset: 0, + + /** + * @type {number} + */ + shadowBlur: 0, + + /** + * @type {number} + */ + shadowOffsetX: 0, + + /** + * @type {number} + */ + shadowOffsetY: 0, + + /** + * @type {number} + */ + lineWidth: 1, + + /** + * If stroke ignore scale + * @type {Boolean} + */ + strokeNoScale: false, + + // Bounding rect text configuration + // Not affected by element transform + /** + * @type {string} + */ + text: null, + + /** + * @type {string} + */ + textFill: '#000', + + /** + * @type {string} + */ + textStroke: null, + + /** + * 'inside', 'left', 'right', 'top', 'bottom' + * [x, y] + * @type {string|Array.} + * @default 'inside' + */ + textPosition: 'inside', + + /** + * @type {string} + */ + textBaseline: null, + + /** + * @type {string} + */ + textAlign: null, + + /** + * @type {string} + */ + textVerticalAlign: null, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textDistance: 5, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowBlur: 0, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowOffsetX: 0, + + /** + * Only useful in Path and Image element + * @type {number} + */ + textShadowOffsetY: 0, + + /** + * If transform text + * Only useful in Path and Image element + * @type {boolean} + */ + textTransform: false, + + /** + * Text rotate around position of Path or Image + * Only useful in Path and Image element and textTransform is false. + */ + textRotation: 0, + + /** + * @type {string} + * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation + */ + blend: null, + + /** + * @param {CanvasRenderingContext2D} ctx + */ + bind: function (ctx, el, prevEl) { + var style = this; + var prevStyle = prevEl && prevEl.style; + var firstDraw = !prevStyle; + + for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { + var prop = STYLE_COMMON_PROPS[i]; + var styleName = prop[0]; + + if (firstDraw || style[styleName] !== prevStyle[styleName]) { + // FIXME Invalid property value will cause style leak from previous element. + ctx[styleName] = style[styleName] || prop[1]; + } + } + + if ((firstDraw || style.fill !== prevStyle.fill)) { + ctx.fillStyle = style.fill; + } + if ((firstDraw || style.stroke !== prevStyle.stroke)) { + ctx.strokeStyle = style.stroke; + } + if ((firstDraw || style.opacity !== prevStyle.opacity)) { + ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; + } + + if ((firstDraw || style.blend !== prevStyle.blend)) { + ctx.globalCompositeOperation = style.blend || 'source-over'; + } + if (this.hasStroke()) { + var lineWidth = style.lineWidth; + ctx.lineWidth = lineWidth / ( + (this.strokeNoScale && el && el.getLineScale) ? el.getLineScale() : 1 + ); + } + }, + + hasFill: function () { + var fill = this.fill; + return fill != null && fill !== 'none'; + }, + + hasStroke: function () { + var stroke = this.stroke; + return stroke != null && stroke !== 'none' && this.lineWidth > 0; + }, + + /** + * Extend from other style + * @param {zrender/graphic/Style} otherStyle + * @param {boolean} overwrite + */ + extendFrom: function (otherStyle, overwrite) { + if (otherStyle) { + var target = this; + for (var name in otherStyle) { + if (otherStyle.hasOwnProperty(name) + && (overwrite || ! target.hasOwnProperty(name)) + ) { + target[name] = otherStyle[name]; + } + } + } + }, + + /** + * Batch setting style with a given object + * @param {Object|string} obj + * @param {*} [obj] + */ + set: function (obj, value) { + if (typeof obj === 'string') { + this[obj] = value; + } + else { + this.extendFrom(obj, true); + } + }, + + /** + * Clone + * @return {zrender/graphic/Style} [description] + */ + clone: function () { + var newStyle = new this.constructor(); + newStyle.extendFrom(this, true); + return newStyle; + }, + + getGradient: function (ctx, obj, rect) { + var method = obj.type === 'radial' ? createRadialGradient : createLinearGradient; + var canvasGradient = method(ctx, obj, rect); + var colorStops = obj.colorStops; + for (var i = 0; i < colorStops.length; i++) { + canvasGradient.addColorStop( + colorStops[i].offset, colorStops[i].color + ); + } + return canvasGradient; + } + }; + + var styleProto = Style.prototype; + for (var i = 0; i < STYLE_COMMON_PROPS.length; i++) { + var prop = STYLE_COMMON_PROPS[i]; + if (!(prop[0] in styleProto)) { + styleProto[prop[0]] = prop[1]; + } + } + + // Provide for others + Style.getGradient = styleProto.getGradient; + + module.exports = Style; + + +/***/ }, +/* 48 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Mixin for drawing text in a element bounding rect + * @module zrender/mixin/RectText + */ + + + + var textContain = __webpack_require__(8); + var BoundingRect = __webpack_require__(9); + + var tmpRect = new BoundingRect(); + + var RectText = function () {}; + + function parsePercent(value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; + } + + RectText.prototype = { + + constructor: RectText, + + /** + * Draw text in a rect with specified position. + * @param {CanvasRenderingContext} ctx + * @param {Object} rect Displayable rect + * @return {Object} textRect Alternative precalculated text bounding rect + */ + drawRectText: function (ctx, rect, textRect) { + var style = this.style; + var text = style.text; + // Convert to string + text != null && (text += ''); + if (!text) { + return; + } + + // FIXME + ctx.save(); + + var x; + var y; + var textPosition = style.textPosition; + var distance = style.textDistance; + var align = style.textAlign; + var font = style.textFont || style.font; + var baseline = style.textBaseline; + var verticalAlign = style.textVerticalAlign; + + textRect = textRect || textContain.getBoundingRect(text, font, align, baseline); + + // Transform rect to view space + var transform = this.transform; + if (!style.textTransform) { + if (transform) { + tmpRect.copy(rect); + tmpRect.applyTransform(transform); + rect = tmpRect; + } + } + else { + this.setTransform(ctx); + } + + // Text position represented by coord + if (textPosition instanceof Array) { + // Percent + x = rect.x + parsePercent(textPosition[0], rect.width); + y = rect.y + parsePercent(textPosition[1], rect.height); + align = align || 'left'; + baseline = baseline || 'top'; + + if (verticalAlign) { + switch (verticalAlign) { + case 'middle': + y -= textRect.height / 2 - textRect.lineHeight / 2; + break; + case 'bottom': + y -= textRect.height - textRect.lineHeight / 2; + break; + default: + y += textRect.lineHeight / 2; + } + // Force bseline to be middle + baseline = 'middle'; + } + } + else { + var res = textContain.adjustTextPositionOnRect( + textPosition, rect, textRect, distance + ); + x = res.x; + y = res.y; + // Default align and baseline when has textPosition + align = align || res.textAlign; + baseline = baseline || res.textBaseline; + } + + // Use canvas default left textAlign. Giving invalid value will cause state not change + ctx.textAlign = align || 'left'; + // Use canvas default alphabetic baseline + ctx.textBaseline = baseline || 'alphabetic'; + + var textFill = style.textFill; + var textStroke = style.textStroke; + textFill && (ctx.fillStyle = textFill); + textStroke && (ctx.strokeStyle = textStroke); + + // TODO Invalid font + ctx.font = font || '12px sans-serif'; + + // Text shadow + // Always set shadowBlur and shadowOffset to avoid leak from displayable + ctx.shadowBlur = style.textShadowBlur; + ctx.shadowColor = style.textShadowColor || 'transparent'; + ctx.shadowOffsetX = style.textShadowOffsetX; + ctx.shadowOffsetY = style.textShadowOffsetY; + + var textLines = text.split('\n'); + + if (style.textRotation) { + transform && ctx.translate(transform[4], transform[5]); + ctx.rotate(style.textRotation); + transform && ctx.translate(-transform[4], -transform[5]); + } + + for (var i = 0; i < textLines.length; i++) { + textFill && ctx.fillText(textLines[i], x, y); + textStroke && ctx.strokeText(textLines[i], x, y); + y += textRect.lineHeight; + } + + ctx.restore(); + } + }; + + module.exports = RectText; + + +/***/ }, +/* 49 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中 + * 可以用于 isInsidePath 判断以及获取boundingRect + * + * @module zrender/core/PathProxy + * @author Yi Shen (http://www.github.com/pissang) + */ + + // TODO getTotalLength, getPointAtLength + + + var curve = __webpack_require__(50); + var vec2 = __webpack_require__(10); + var bbox = __webpack_require__(51); + var BoundingRect = __webpack_require__(9); + var dpr = __webpack_require__(41).devicePixelRatio; + + var CMD = { + M: 1, + L: 2, + C: 3, + Q: 4, + A: 5, + Z: 6, + // Rect + R: 7 + }; + + var min = []; + var max = []; + var min2 = []; + var max2 = []; + var mathMin = Math.min; + var mathMax = Math.max; + var mathCos = Math.cos; + var mathSin = Math.sin; + var mathSqrt = Math.sqrt; + var mathAbs = Math.abs; + + var hasTypedArray = typeof Float32Array != 'undefined'; + + /** + * @alias module:zrender/core/PathProxy + * @constructor + */ + var PathProxy = function () { + + /** + * Path data. Stored as flat array + * @type {Array.} + */ + this.data = []; + + this._len = 0; + + this._ctx = null; + + this._xi = 0; + this._yi = 0; + + this._x0 = 0; + this._y0 = 0; + + // Unit x, Unit y. Provide for avoiding drawing that too short line segment + this._ux = 0; + this._uy = 0; + }; + + /** + * 快速计算Path包围盒(并不是最小包围盒) + * @return {Object} + */ + PathProxy.prototype = { + + constructor: PathProxy, + + _lineDash: null, + + _dashOffset: 0, + + _dashIdx: 0, + + _dashSum: 0, + + /** + * @readOnly + */ + setScale: function (sx, sy) { + this._ux = mathAbs(1 / dpr / sx) || 0; + this._uy = mathAbs(1 / dpr / sy) || 0; + }, + + getContext: function () { + return this._ctx; + }, + + /** + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + beginPath: function (ctx) { + + this._ctx = ctx; + + ctx && ctx.beginPath(); + + ctx && (this.dpr = ctx.dpr); + + // Reset + this._len = 0; + + if (this._lineDash) { + this._lineDash = null; + + this._dashOffset = 0; + } + + return this; + }, + + /** + * @param {number} x + * @param {number} y + * @return {module:zrender/core/PathProxy} + */ + moveTo: function (x, y) { + this.addData(CMD.M, x, y); + this._ctx && this._ctx.moveTo(x, y); + + // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用 + // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。 + // 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要 + // 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持 + this._x0 = x; + this._y0 = y; + + this._xi = x; + this._yi = y; + + return this; + }, + + /** + * @param {number} x + * @param {number} y + * @return {module:zrender/core/PathProxy} + */ + lineTo: function (x, y) { + var exceedUnit = mathAbs(x - this._xi) > this._ux + || mathAbs(y - this._yi) > this._uy + // Force draw the first segment + || this._len < 5; + + this.addData(CMD.L, x, y); + + if (this._ctx && exceedUnit) { + this._needsDash() ? this._dashedLineTo(x, y) + : this._ctx.lineTo(x, y); + } + if (exceedUnit) { + this._xi = x; + this._yi = y; + } + + return this; + }, + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @return {module:zrender/core/PathProxy} + */ + bezierCurveTo: function (x1, y1, x2, y2, x3, y3) { + this.addData(CMD.C, x1, y1, x2, y2, x3, y3); + if (this._ctx) { + this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) + : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); + } + this._xi = x3; + this._yi = y3; + return this; + }, + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {module:zrender/core/PathProxy} + */ + quadraticCurveTo: function (x1, y1, x2, y2) { + this.addData(CMD.Q, x1, y1, x2, y2); + if (this._ctx) { + this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) + : this._ctx.quadraticCurveTo(x1, y1, x2, y2); + } + this._xi = x2; + this._yi = y2; + return this; + }, + + /** + * @param {number} cx + * @param {number} cy + * @param {number} r + * @param {number} startAngle + * @param {number} endAngle + * @param {boolean} anticlockwise + * @return {module:zrender/core/PathProxy} + */ + arc: function (cx, cy, r, startAngle, endAngle, anticlockwise) { + this.addData( + CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1 + ); + this._ctx && this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); + + this._xi = mathCos(endAngle) * r + cx; + this._xi = mathSin(endAngle) * r + cx; + return this; + }, + + // TODO + arcTo: function (x1, y1, x2, y2, radius) { + if (this._ctx) { + this._ctx.arcTo(x1, y1, x2, y2, radius); + } + return this; + }, + + // TODO + rect: function (x, y, w, h) { + this._ctx && this._ctx.rect(x, y, w, h); + this.addData(CMD.R, x, y, w, h); + return this; + }, + + /** + * @return {module:zrender/core/PathProxy} + */ + closePath: function () { + this.addData(CMD.Z); + + var ctx = this._ctx; + var x0 = this._x0; + var y0 = this._y0; + if (ctx) { + this._needsDash() && this._dashedLineTo(x0, y0); + ctx.closePath(); + } + + this._xi = x0; + this._yi = y0; + return this; + }, + + /** + * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。 + * stroke 同样 + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + fill: function (ctx) { + ctx && ctx.fill(); + this.toStatic(); + }, + + /** + * @param {CanvasRenderingContext2D} ctx + * @return {module:zrender/core/PathProxy} + */ + stroke: function (ctx) { + ctx && ctx.stroke(); + this.toStatic(); + }, + + /** + * 必须在其它绘制命令前调用 + * Must be invoked before all other path drawing methods + * @return {module:zrender/core/PathProxy} + */ + setLineDash: function (lineDash) { + if (lineDash instanceof Array) { + this._lineDash = lineDash; + + this._dashIdx = 0; + + var lineDashSum = 0; + for (var i = 0; i < lineDash.length; i++) { + lineDashSum += lineDash[i]; + } + this._dashSum = lineDashSum; + } + return this; + }, + + /** + * 必须在其它绘制命令前调用 + * Must be invoked before all other path drawing methods + * @return {module:zrender/core/PathProxy} + */ + setLineDashOffset: function (offset) { + this._dashOffset = offset; + return this; + }, + + /** + * + * @return {boolean} + */ + len: function () { + return this._len; + }, + + /** + * 直接设置 Path 数据 + */ + setData: function (data) { + + var len = data.length; + + if (! (this.data && this.data.length == len) && hasTypedArray) { + this.data = new Float32Array(len); + } + + for (var i = 0; i < len; i++) { + this.data[i] = data[i]; + } + + this._len = len; + }, + + /** + * 添加子路径 + * @param {module:zrender/core/PathProxy|Array.} path + */ + appendPath: function (path) { + if (!(path instanceof Array)) { + path = [path]; + } + var len = path.length; + var appendSize = 0; + var offset = this._len; + for (var i = 0; i < len; i++) { + appendSize += path[i].len(); + } + if (hasTypedArray && (this.data instanceof Float32Array)) { + this.data = new Float32Array(offset + appendSize); + } + for (var i = 0; i < len; i++) { + var appendPathData = path[i].data; + for (var k = 0; k < appendPathData.length; k++) { + this.data[offset++] = appendPathData[k]; + } + } + this._len = offset; + }, + + /** + * 填充 Path 数据。 + * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。 + */ + addData: function (cmd) { + var data = this.data; + if (this._len + arguments.length > data.length) { + // 因为之前的数组已经转换成静态的 Float32Array + // 所以不够用时需要扩展一个新的动态数组 + this._expandData(); + data = this.data; + } + for (var i = 0; i < arguments.length; i++) { + data[this._len++] = arguments[i]; + } + + this._prevCmd = cmd; + }, + + _expandData: function () { + // Only if data is Float32Array + if (!(this.data instanceof Array)) { + var newData = []; + for (var i = 0; i < this._len; i++) { + newData[i] = this.data[i]; + } + this.data = newData; + } + }, + + /** + * If needs js implemented dashed line + * @return {boolean} + * @private + */ + _needsDash: function () { + return this._lineDash; + }, + + _dashedLineTo: function (x1, y1) { + var dashSum = this._dashSum; + var offset = this._dashOffset; + var lineDash = this._lineDash; + var ctx = this._ctx; + + var x0 = this._xi; + var y0 = this._yi; + var dx = x1 - x0; + var dy = y1 - y0; + var dist = mathSqrt(dx * dx + dy * dy); + var x = x0; + var y = y0; + var dash; + var nDash = lineDash.length; + var idx; + dx /= dist; + dy /= dist; + + if (offset < 0) { + // Convert to positive offset + offset = dashSum + offset; + } + offset %= dashSum; + x -= offset * dx; + y -= offset * dy; + + while ((dx > 0 && x <= x1) || (dx < 0 && x >= x1) + || (dx == 0 && ((dy > 0 && y <= y1) || (dy < 0 && y >= y1)))) { + idx = this._dashIdx; + dash = lineDash[idx]; + x += dx * dash; + y += dy * dash; + this._dashIdx = (idx + 1) % nDash; + // Skip positive offset + if ((dx > 0 && x < x0) || (dx < 0 && x > x0) || (dy > 0 && y < y0) || (dy < 0 && y > y0)) { + continue; + } + ctx[idx % 2 ? 'moveTo' : 'lineTo']( + dx >= 0 ? mathMin(x, x1) : mathMax(x, x1), + dy >= 0 ? mathMin(y, y1) : mathMax(y, y1) + ); + } + // Offset for next lineTo + dx = x - x1; + dy = y - y1; + this._dashOffset = -mathSqrt(dx * dx + dy * dy); + }, + + // Not accurate dashed line to + _dashedBezierTo: function (x1, y1, x2, y2, x3, y3) { + var dashSum = this._dashSum; + var offset = this._dashOffset; + var lineDash = this._lineDash; + var ctx = this._ctx; + + var x0 = this._xi; + var y0 = this._yi; + var t; + var dx; + var dy; + var cubicAt = curve.cubicAt; + var bezierLen = 0; + var idx = this._dashIdx; + var nDash = lineDash.length; + + var x; + var y; + + var tmpLen = 0; + + if (offset < 0) { + // Convert to positive offset + offset = dashSum + offset; + } + offset %= dashSum; + // Bezier approx length + for (t = 0; t < 1; t += 0.1) { + dx = cubicAt(x0, x1, x2, x3, t + 0.1) + - cubicAt(x0, x1, x2, x3, t); + dy = cubicAt(y0, y1, y2, y3, t + 0.1) + - cubicAt(y0, y1, y2, y3, t); + bezierLen += mathSqrt(dx * dx + dy * dy); + } + + // Find idx after add offset + for (; idx < nDash; idx++) { + tmpLen += lineDash[idx]; + if (tmpLen > offset) { + break; + } + } + t = (tmpLen - offset) / bezierLen; + + while (t <= 1) { + + x = cubicAt(x0, x1, x2, x3, t); + y = cubicAt(y0, y1, y2, y3, t); + + // Use line to approximate dashed bezier + // Bad result if dash is long + idx % 2 ? ctx.moveTo(x, y) + : ctx.lineTo(x, y); + + t += lineDash[idx] / bezierLen; + + idx = (idx + 1) % nDash; + } + + // Finish the last segment and calculate the new offset + (idx % 2 !== 0) && ctx.lineTo(x3, y3); + dx = x3 - x; + dy = y3 - y; + this._dashOffset = -mathSqrt(dx * dx + dy * dy); + }, + + _dashedQuadraticTo: function (x1, y1, x2, y2) { + // Convert quadratic to cubic using degree elevation + var x3 = x2; + var y3 = y2; + x2 = (x2 + 2 * x1) / 3; + y2 = (y2 + 2 * y1) / 3; + x1 = (this._xi + 2 * x1) / 3; + y1 = (this._yi + 2 * y1) / 3; + + this._dashedBezierTo(x1, y1, x2, y2, x3, y3); + }, + + /** + * 转成静态的 Float32Array 减少堆内存占用 + * Convert dynamic array to static Float32Array + */ + toStatic: function () { + var data = this.data; + if (data instanceof Array) { + data.length = this._len; + if (hasTypedArray) { + this.data = new Float32Array(data); + } + } + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function () { + min[0] = min[1] = min2[0] = min2[1] = Number.MAX_VALUE; + max[0] = max[1] = max2[0] = max2[1] = -Number.MAX_VALUE; + + var data = this.data; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = data[i]; + yi = data[i + 1]; + + x0 = xi; + y0 = yi; + } + + switch (cmd) { + case CMD.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + min2[0] = x0; + min2[1] = y0; + max2[0] = x0; + max2[1] = y0; + break; + case CMD.L: + bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + bbox.fromCubic( + xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + min2, max2 + ); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.Q: + bbox.fromQuadratic( + xi, yi, data[i++], data[i++], data[i], data[i + 1], + min2, max2 + ); + xi = data[i++]; + yi = data[i++]; + break; + case CMD.A: + // TODO Arc 判断的开销比较大 + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++]; + var endAngle = data[i++] + startAngle; + // TODO Arc 旋转 + var psi = data[i++]; + var anticlockwise = 1 - data[i++]; + + if (i == 1) { + // 直接使用 arc 命令 + // 第一个命令起点还未定义 + x0 = mathCos(startAngle) * rx + cx; + y0 = mathSin(startAngle) * ry + cy; + } + + bbox.fromArc( + cx, cy, rx, ry, startAngle, endAngle, + anticlockwise, min2, max2 + ); + + xi = mathCos(endAngle) * rx + cx; + yi = mathSin(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + // Use fromLine + bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2); + break; + case CMD.Z: + xi = x0; + yi = y0; + break; + } + + // Union + vec2.min(min, min, min2); + vec2.max(max, max, max2); + } + + // No data + if (i === 0) { + min[0] = min[1] = max[0] = max[1] = 0; + } + + return new BoundingRect( + min[0], min[1], max[0] - min[0], max[1] - min[1] + ); + }, + + /** + * Rebuild path from current data + * Rebuild path will not consider javascript implemented line dash. + * @param {CanvasRenderingContext} ctx + */ + rebuildPath: function (ctx) { + var d = this.data; + var x0, y0; + var xi, yi; + var x, y; + var ux = this._ux; + var uy = this._uy; + var len = this._len; + for (var i = 0; i < len;) { + var cmd = d[i++]; + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = d[i]; + yi = d[i + 1]; + + x0 = xi; + y0 = yi; + } + switch (cmd) { + case CMD.M: + x0 = xi = d[i++]; + y0 = yi = d[i++]; + ctx.moveTo(xi, yi); + break; + case CMD.L: + x = d[i++]; + y = d[i++]; + // Not draw too small seg between + if (mathAbs(x - xi) > ux || mathAbs(y - yi) > uy || i === len - 1) { + ctx.lineTo(x, y); + xi = x; + yi = y; + } + break; + case CMD.C: + ctx.bezierCurveTo( + d[i++], d[i++], d[i++], d[i++], d[i++], d[i++] + ); + xi = d[i - 2]; + yi = d[i - 1]; + break; + case CMD.Q: + ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]); + xi = d[i - 2]; + yi = d[i - 1]; + break; + case CMD.A: + var cx = d[i++]; + var cy = d[i++]; + var rx = d[i++]; + var ry = d[i++]; + var theta = d[i++]; + var dTheta = d[i++]; + var psi = d[i++]; + var fs = d[i++]; + var r = (rx > ry) ? rx : ry; + var scaleX = (rx > ry) ? 1 : rx / ry; + var scaleY = (rx > ry) ? ry / rx : 1; + var isEllipse = Math.abs(rx - ry) > 1e-3; + var endAngle = theta + dTheta; + if (isEllipse) { + ctx.translate(cx, cy); + ctx.rotate(psi); + ctx.scale(scaleX, scaleY); + ctx.arc(0, 0, r, theta, endAngle, 1 - fs); + ctx.scale(1 / scaleX, 1 / scaleY); + ctx.rotate(-psi); + ctx.translate(-cx, -cy); + } + else { + ctx.arc(cx, cy, r, theta, endAngle, 1 - fs); + } + + if (i == 1) { + // 直接使用 arc 命令 + // 第一个命令起点还未定义 + x0 = mathCos(theta) * rx + cx; + y0 = mathSin(theta) * ry + cy; + } + xi = mathCos(endAngle) * rx + cx; + yi = mathSin(endAngle) * ry + cy; + break; + case CMD.R: + x0 = xi = d[i]; + y0 = yi = d[i + 1]; + ctx.rect(d[i++], d[i++], d[i++], d[i++]); + break; + case CMD.Z: + ctx.closePath(); + xi = x0; + yi = y0; + } + } + } + }; + + PathProxy.CMD = CMD; + + module.exports = PathProxy; + + +/***/ }, +/* 50 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 曲线辅助模块 + * @module zrender/core/curve + * @author pissang(https://www.github.com/pissang) + */ + + + var vec2 = __webpack_require__(10); + var v2Create = vec2.create; + var v2DistSquare = vec2.distSquare; + var mathPow = Math.pow; + var mathSqrt = Math.sqrt; + + var EPSILON = 1e-8; + var EPSILON_NUMERIC = 1e-4; + + var THREE_SQRT = mathSqrt(3); + var ONE_THIRD = 1 / 3; + + // 临时变量 + var _v0 = v2Create(); + var _v1 = v2Create(); + var _v2 = v2Create(); + // var _v3 = vec2.create(); + + function isAroundZero(val) { + return val > -EPSILON && val < EPSILON; + } + function isNotAroundZero(val) { + return val > EPSILON || val < -EPSILON; + } + /** + * 计算三次贝塞尔值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @return {number} + */ + function cubicAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return onet * onet * (onet * p0 + 3 * t * p1) + + t * t * (t * p3 + 3 * onet * p2); + } + + /** + * 计算三次贝塞尔导数值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @return {number} + */ + function cubicDerivativeAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return 3 * ( + ((p1 - p0) * onet + 2 * (p2 - p1) * t) * onet + + (p3 - p2) * t * t + ); + } + + /** + * 计算三次贝塞尔方程根,使用盛金公式 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} val + * @param {Array.} roots + * @return {number} 有效根数目 + */ + function cubicRootAt(p0, p1, p2, p3, val, roots) { + // Evaluate roots of cubic functions + var a = p3 + 3 * (p1 - p2) - p0; + var b = 3 * (p2 - p1 * 2 + p0); + var c = 3 * (p1 - p0); + var d = p0 - val; + + var A = b * b - 3 * a * c; + var B = b * c - 9 * a * d; + var C = c * c - 3 * b * d; + + var n = 0; + + if (isAroundZero(A) && isAroundZero(B)) { + if (isAroundZero(b)) { + roots[0] = 0; + } + else { + var t1 = -c / b; //t1, t2, t3, b is not zero + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = B * B - 4 * A * C; + + if (isAroundZero(disc)) { + var K = B / A; + var t1 = -b / a + K; // t1, a is not zero + var t2 = -K / 2; // t2, t3 + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var Y1 = A * b + 1.5 * a * (-B + discSqrt); + var Y2 = A * b + 1.5 * a * (-B - discSqrt); + if (Y1 < 0) { + Y1 = -mathPow(-Y1, ONE_THIRD); + } + else { + Y1 = mathPow(Y1, ONE_THIRD); + } + if (Y2 < 0) { + Y2 = -mathPow(-Y2, ONE_THIRD); + } + else { + Y2 = mathPow(Y2, ONE_THIRD); + } + var t1 = (-b - (Y1 + Y2)) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else { + var T = (2 * A * b - 3 * a * B) / (2 * mathSqrt(A * A * A)); + var theta = Math.acos(T) / 3; + var ASqrt = mathSqrt(A); + var tmp = Math.cos(theta); + + var t1 = (-b - 2 * ASqrt * tmp) / (3 * a); + var t2 = (-b + ASqrt * (tmp + THREE_SQRT * Math.sin(theta))) / (3 * a); + var t3 = (-b + ASqrt * (tmp - THREE_SQRT * Math.sin(theta))) / (3 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + if (t3 >= 0 && t3 <= 1) { + roots[n++] = t3; + } + } + } + return n; + } + + /** + * 计算三次贝塞尔方程极限值的位置 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {Array.} extrema + * @return {number} 有效数目 + */ + function cubicExtrema(p0, p1, p2, p3, extrema) { + var b = 6 * p2 - 12 * p1 + 6 * p0; + var a = 9 * p1 + 3 * p3 - 3 * p0 - 9 * p2; + var c = 3 * p1 - 3 * p0; + + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <=1) { + extrema[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + extrema[0] = -b / (2 * a); + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + extrema[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + extrema[n++] = t2; + } + } + } + return n; + } + + /** + * 细分三次贝塞尔曲线 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} p3 + * @param {number} t + * @param {Array.} out + */ + function cubicSubdivide(p0, p1, p2, p3, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p23 = (p3 - p2) * t + p2; + + var p012 = (p12 - p01) * t + p01; + var p123 = (p23 - p12) * t + p12; + + var p0123 = (p123 - p012) * t + p012; + // Seg0 + out[0] = p0; + out[1] = p01; + out[2] = p012; + out[3] = p0123; + // Seg1 + out[4] = p0123; + out[5] = p123; + out[6] = p23; + out[7] = p3; + } + + /** + * 投射点到三次贝塞尔曲线上,返回投射距离。 + * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {number} x + * @param {number} y + * @param {Array.} [out] 投射点 + * @return {number} + */ + function cubicProjectPoint( + x0, y0, x1, y1, x2, y2, x3, y3, + x, y, out + ) { + // http://pomax.github.io/bezierinfo/#projections + var t; + var interval = 0.005; + var d = Infinity; + var prev; + var next; + var d1; + var d2; + + _v0[0] = x; + _v0[1] = y; + + // 先粗略估计一下可能的最小距离的 t 值 + // PENDING + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = cubicAt(x0, x1, x2, x3, _t); + _v1[1] = cubicAt(y0, y1, y2, y3, _t); + d1 = v2DistSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + + // At most 32 iteration + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + prev = t - interval; + next = t + interval; + // t - interval + _v1[0] = cubicAt(x0, x1, x2, x3, prev); + _v1[1] = cubicAt(y0, y1, y2, y3, prev); + + d1 = v2DistSquare(_v1, _v0); + + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + // t + interval + _v2[0] = cubicAt(x0, x1, x2, x3, next); + _v2[1] = cubicAt(y0, y1, y2, y3, next); + d2 = v2DistSquare(_v2, _v0); + + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + // t + if (out) { + out[0] = cubicAt(x0, x1, x2, x3, t); + out[1] = cubicAt(y0, y1, y2, y3, t); + } + // console.log(interval, i); + return mathSqrt(d); + } + + /** + * 计算二次方贝塞尔值 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @return {number} + */ + function quadraticAt(p0, p1, p2, t) { + var onet = 1 - t; + return onet * (onet * p0 + 2 * t * p1) + t * t * p2; + } + + /** + * 计算二次方贝塞尔导数值 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @return {number} + */ + function quadraticDerivativeAt(p0, p1, p2, t) { + return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); + } + + /** + * 计算二次方贝塞尔方程根 + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @param {Array.} roots + * @return {number} 有效根数目 + */ + function quadraticRootAt(p0, p1, p2, val, roots) { + var a = p0 - 2 * p1 + p2; + var b = 2 * (p1 - p0); + var c = p0 - val; + + var n = 0; + if (isAroundZero(a)) { + if (isNotAroundZero(b)) { + var t1 = -c / b; + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + } + else { + var disc = b * b - 4 * a * c; + if (isAroundZero(disc)) { + var t1 = -b / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + } + else if (disc > 0) { + var discSqrt = mathSqrt(disc); + var t1 = (-b + discSqrt) / (2 * a); + var t2 = (-b - discSqrt) / (2 * a); + if (t1 >= 0 && t1 <= 1) { + roots[n++] = t1; + } + if (t2 >= 0 && t2 <= 1) { + roots[n++] = t2; + } + } + } + return n; + } + + /** + * 计算二次贝塞尔方程极限值 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @return {number} + */ + function quadraticExtremum(p0, p1, p2) { + var divider = p0 + p2 - 2 * p1; + if (divider === 0) { + // p1 is center of p0 and p2 + return 0.5; + } + else { + return (p0 - p1) / divider; + } + } + + /** + * 细分二次贝塞尔曲线 + * @memberOf module:zrender/core/curve + * @param {number} p0 + * @param {number} p1 + * @param {number} p2 + * @param {number} t + * @param {Array.} out + */ + function quadraticSubdivide(p0, p1, p2, t, out) { + var p01 = (p1 - p0) * t + p0; + var p12 = (p2 - p1) * t + p1; + var p012 = (p12 - p01) * t + p01; + + // Seg0 + out[0] = p0; + out[1] = p01; + out[2] = p012; + + // Seg1 + out[3] = p012; + out[4] = p12; + out[5] = p2; + } + + /** + * 投射点到二次贝塞尔曲线上,返回投射距离。 + * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x + * @param {number} y + * @param {Array.} out 投射点 + * @return {number} + */ + function quadraticProjectPoint( + x0, y0, x1, y1, x2, y2, + x, y, out + ) { + // http://pomax.github.io/bezierinfo/#projections + var t; + var interval = 0.005; + var d = Infinity; + + _v0[0] = x; + _v0[1] = y; + + // 先粗略估计一下可能的最小距离的 t 值 + // PENDING + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = quadraticAt(x0, x1, x2, _t); + _v1[1] = quadraticAt(y0, y1, y2, _t); + var d1 = v2DistSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + + // At most 32 iteration + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + var prev = t - interval; + var next = t + interval; + // t - interval + _v1[0] = quadraticAt(x0, x1, x2, prev); + _v1[1] = quadraticAt(y0, y1, y2, prev); + + var d1 = v2DistSquare(_v1, _v0); + + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } + else { + // t + interval + _v2[0] = quadraticAt(x0, x1, x2, next); + _v2[1] = quadraticAt(y0, y1, y2, next); + var d2 = v2DistSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } + else { + interval *= 0.5; + } + } + } + // t + if (out) { + out[0] = quadraticAt(x0, x1, x2, t); + out[1] = quadraticAt(y0, y1, y2, t); + } + // console.log(interval, i); + return mathSqrt(d); + } + + module.exports = { + + cubicAt: cubicAt, + + cubicDerivativeAt: cubicDerivativeAt, + + cubicRootAt: cubicRootAt, + + cubicExtrema: cubicExtrema, + + cubicSubdivide: cubicSubdivide, + + cubicProjectPoint: cubicProjectPoint, + + quadraticAt: quadraticAt, + + quadraticDerivativeAt: quadraticDerivativeAt, + + quadraticRootAt: quadraticRootAt, + + quadraticExtremum: quadraticExtremum, + + quadraticSubdivide: quadraticSubdivide, + + quadraticProjectPoint: quadraticProjectPoint + }; + + +/***/ }, +/* 51 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @author Yi Shen(https://github.com/pissang) + */ + + + var vec2 = __webpack_require__(10); + var curve = __webpack_require__(50); + + var bbox = {}; + var mathMin = Math.min; + var mathMax = Math.max; + var mathSin = Math.sin; + var mathCos = Math.cos; + + var start = vec2.create(); + var end = vec2.create(); + var extremity = vec2.create(); + + var PI2 = Math.PI * 2; + /** + * 从顶点数组中计算出最小包围盒,写入`min`和`max`中 + * @module zrender/core/bbox + * @param {Array} points 顶点数组 + * @param {number} min + * @param {number} max + */ + bbox.fromPoints = function(points, min, max) { + if (points.length === 0) { + return; + } + var p = points[0]; + var left = p[0]; + var right = p[0]; + var top = p[1]; + var bottom = p[1]; + var i; + + for (i = 1; i < points.length; i++) { + p = points[i]; + left = mathMin(left, p[0]); + right = mathMax(right, p[0]); + top = mathMin(top, p[1]); + bottom = mathMax(bottom, p[1]); + } + + min[0] = left; + min[1] = top; + max[0] = right; + max[1] = bottom; + }; + + /** + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromLine = function (x0, y0, x1, y1, min, max) { + min[0] = mathMin(x0, x1); + min[1] = mathMin(y0, y1); + max[0] = mathMax(x0, x1); + max[1] = mathMax(y0, y1); + }; + + var xDim = []; + var yDim = []; + /** + * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中 + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromCubic = function( + x0, y0, x1, y1, x2, y2, x3, y3, min, max + ) { + var cubicExtrema = curve.cubicExtrema; + var cubicAt = curve.cubicAt; + var i; + var n = cubicExtrema(x0, x1, x2, x3, xDim); + min[0] = Infinity; + min[1] = Infinity; + max[0] = -Infinity; + max[1] = -Infinity; + + for (i = 0; i < n; i++) { + var x = cubicAt(x0, x1, x2, x3, xDim[i]); + min[0] = mathMin(x, min[0]); + max[0] = mathMax(x, max[0]); + } + n = cubicExtrema(y0, y1, y2, y3, yDim); + for (i = 0; i < n; i++) { + var y = cubicAt(y0, y1, y2, y3, yDim[i]); + min[1] = mathMin(y, min[1]); + max[1] = mathMax(y, max[1]); + } + + min[0] = mathMin(x0, min[0]); + max[0] = mathMax(x0, max[0]); + min[0] = mathMin(x3, min[0]); + max[0] = mathMax(x3, max[0]); + + min[1] = mathMin(y0, min[1]); + max[1] = mathMax(y0, max[1]); + min[1] = mathMin(y3, min[1]); + max[1] = mathMax(y3, max[1]); + }; + + /** + * 从二阶贝塞尔曲线(p0, p1, p2)中计算出最小包围盒,写入`min`和`max`中 + * @memberOf module:zrender/core/bbox + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromQuadratic = function(x0, y0, x1, y1, x2, y2, min, max) { + var quadraticExtremum = curve.quadraticExtremum; + var quadraticAt = curve.quadraticAt; + // Find extremities, where derivative in x dim or y dim is zero + var tx = + mathMax( + mathMin(quadraticExtremum(x0, x1, x2), 1), 0 + ); + var ty = + mathMax( + mathMin(quadraticExtremum(y0, y1, y2), 1), 0 + ); + + var x = quadraticAt(x0, x1, x2, tx); + var y = quadraticAt(y0, y1, y2, ty); + + min[0] = mathMin(x0, x2, x); + min[1] = mathMin(y0, y2, y); + max[0] = mathMax(x0, x2, x); + max[1] = mathMax(y0, y2, y); + }; + + /** + * 从圆弧中计算出最小包围盒,写入`min`和`max`中 + * @method + * @memberOf module:zrender/core/bbox + * @param {number} x + * @param {number} y + * @param {number} rx + * @param {number} ry + * @param {number} startAngle + * @param {number} endAngle + * @param {number} anticlockwise + * @param {Array.} min + * @param {Array.} max + */ + bbox.fromArc = function ( + x, y, rx, ry, startAngle, endAngle, anticlockwise, min, max + ) { + var vec2Min = vec2.min; + var vec2Max = vec2.max; + + var diff = Math.abs(startAngle - endAngle); + + + if (diff % PI2 < 1e-4 && diff > 1e-4) { + // Is a circle + min[0] = x - rx; + min[1] = y - ry; + max[0] = x + rx; + max[1] = y + ry; + return; + } + + start[0] = mathCos(startAngle) * rx + x; + start[1] = mathSin(startAngle) * ry + y; + + end[0] = mathCos(endAngle) * rx + x; + end[1] = mathSin(endAngle) * ry + y; + + vec2Min(min, start, end); + vec2Max(max, start, end); + + // Thresh to [0, Math.PI * 2] + startAngle = startAngle % (PI2); + if (startAngle < 0) { + startAngle = startAngle + PI2; + } + endAngle = endAngle % (PI2); + if (endAngle < 0) { + endAngle = endAngle + PI2; + } + + if (startAngle > endAngle && !anticlockwise) { + endAngle += PI2; + } + else if (startAngle < endAngle && anticlockwise) { + startAngle += PI2; + } + if (anticlockwise) { + var tmp = endAngle; + endAngle = startAngle; + startAngle = tmp; + } + + // var number = 0; + // var step = (anticlockwise ? -Math.PI : Math.PI) / 2; + for (var angle = 0; angle < endAngle; angle += Math.PI / 2) { + if (angle > startAngle) { + extremity[0] = mathCos(angle) * rx + x; + extremity[1] = mathSin(angle) * ry + y; + + vec2Min(min, extremity, min); + vec2Max(max, extremity, max); + } + } + }; + + module.exports = bbox; + + + +/***/ }, +/* 52 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var CMD = __webpack_require__(49).CMD; + var line = __webpack_require__(53); + var cubic = __webpack_require__(54); + var quadratic = __webpack_require__(55); + var arc = __webpack_require__(56); + var normalizeRadian = __webpack_require__(57).normalizeRadian; + var curve = __webpack_require__(50); + + var windingLine = __webpack_require__(58); + + var containStroke = line.containStroke; + + var PI2 = Math.PI * 2; + + var EPSILON = 1e-4; + + function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON; + } + + // 临时数组 + var roots = [-1, -1, -1]; + var extrema = [-1, -1]; + + function swapExtrema() { + var tmp = extrema[0]; + extrema[0] = extrema[1]; + extrema[1] = tmp; + } + + function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) { + // Quick reject + if ( + (y > y0 && y > y1 && y > y2 && y > y3) + || (y < y0 && y < y1 && y < y2 && y < y3) + ) { + return 0; + } + var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var w = 0; + var nExtrema = -1; + var y0_, y1_; + for (var i = 0; i < nRoots; i++) { + var t = roots[i]; + + // Avoid winding error when intersection point is the connect point of two line of polygon + var unit = (t === 0 || t === 1) ? 0.5 : 1; + + var x_ = curve.cubicAt(x0, x1, x2, x3, t); + if (x_ < x) { // Quick reject + continue; + } + if (nExtrema < 0) { + nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema); + if (extrema[1] < extrema[0] && nExtrema > 1) { + swapExtrema(); + } + y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]); + if (nExtrema > 1) { + y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]); + } + } + if (nExtrema == 2) { + // 分成三段单调函数 + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else if (t < extrema[1]) { + w += y1_ < y0_ ? unit : -unit; + } + else { + w += y3 < y1_ ? unit : -unit; + } + } + else { + // 分成两段单调函数 + if (t < extrema[0]) { + w += y0_ < y0 ? unit : -unit; + } + else { + w += y3 < y0_ ? unit : -unit; + } + } + } + return w; + } + } + + function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) { + // Quick reject + if ( + (y > y0 && y > y1 && y > y2) + || (y < y0 && y < y1 && y < y2) + ) { + return 0; + } + var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots); + if (nRoots === 0) { + return 0; + } + else { + var t = curve.quadraticExtremum(y0, y1, y2); + if (t >= 0 && t <= 1) { + var w = 0; + var y_ = curve.quadraticAt(y0, y1, y2, t); + for (var i = 0; i < nRoots; i++) { + // Remove one endpoint. + var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1; + + var x_ = curve.quadraticAt(x0, x1, x2, roots[i]); + if (x_ < x) { // Quick reject + continue; + } + if (roots[i] < t) { + w += y_ < y0 ? unit : -unit; + } + else { + w += y2 < y_ ? unit : -unit; + } + } + return w; + } + else { + // Remove one endpoint. + var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1; + + var x_ = curve.quadraticAt(x0, x1, x2, roots[0]); + if (x_ < x) { // Quick reject + return 0; + } + return y2 < y0 ? unit : -unit; + } + } + } + + // TODO + // Arc 旋转 + function windingArc( + cx, cy, r, startAngle, endAngle, anticlockwise, x, y + ) { + y -= cy; + if (y > r || y < -r) { + return 0; + } + var tmp = Math.sqrt(r * r - y * y); + roots[0] = -tmp; + roots[1] = tmp; + + var diff = Math.abs(startAngle - endAngle); + if (diff < 1e-4) { + return 0; + } + if (diff % PI2 < 1e-4) { + // Is a circle + startAngle = 0; + endAngle = PI2; + var dir = anticlockwise ? 1 : -1; + if (x >= roots[0] + cx && x <= roots[1] + cx) { + return dir; + } else { + return 0; + } + } + + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } + else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2; + } + + var w = 0; + for (var i = 0; i < 2; i++) { + var x_ = roots[i]; + if (x_ + cx > x) { + var angle = Math.atan2(y, x_); + var dir = anticlockwise ? 1 : -1; + if (angle < 0) { + angle = PI2 + angle; + } + if ( + (angle >= startAngle && angle <= endAngle) + || (angle + PI2 >= startAngle && angle + PI2 <= endAngle) + ) { + if (angle > Math.PI / 2 && angle < Math.PI * 1.5) { + dir = -dir; + } + w += dir; + } + } + } + return w; + } + + function containPath(data, lineWidth, isStroke, x, y) { + var w = 0; + var xi = 0; + var yi = 0; + var x0 = 0; + var y0 = 0; + + for (var i = 0; i < data.length;) { + var cmd = data[i++]; + // Begin a new subpath + if (cmd === CMD.M && i > 1) { + // Close previous subpath + if (!isStroke) { + w += windingLine(xi, yi, x0, y0, x, y); + } + // 如果被任何一个 subpath 包含 + // if (w !== 0) { + // return true; + // } + } + + if (i == 1) { + // 如果第一个命令是 L, C, Q + // 则 previous point 同绘制命令的第一个 point + // + // 第一个命令为 Arc 的情况下会在后面特殊处理 + xi = data[i]; + yi = data[i + 1]; + + x0 = xi; + y0 = yi; + } + + switch (cmd) { + case CMD.M: + // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点 + // 在 closePath 的时候使用 + x0 = data[i++]; + y0 = data[i++]; + xi = x0; + yi = y0; + break; + case CMD.L: + if (isStroke) { + if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) { + return true; + } + } + else { + // NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN + w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.C: + if (isStroke) { + if (cubic.containStroke(xi, yi, + data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + lineWidth, x, y + )) { + return true; + } + } + else { + w += windingCubic( + xi, yi, + data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], + x, y + ) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.Q: + if (isStroke) { + if (quadratic.containStroke(xi, yi, + data[i++], data[i++], data[i], data[i + 1], + lineWidth, x, y + )) { + return true; + } + } + else { + w += windingQuadratic( + xi, yi, + data[i++], data[i++], data[i], data[i + 1], + x, y + ) || 0; + } + xi = data[i++]; + yi = data[i++]; + break; + case CMD.A: + // TODO Arc 判断的开销比较大 + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var theta = data[i++]; + var dTheta = data[i++]; + // TODO Arc 旋转 + var psi = data[i++]; + var anticlockwise = 1 - data[i++]; + var x1 = Math.cos(theta) * rx + cx; + var y1 = Math.sin(theta) * ry + cy; + // 不是直接使用 arc 命令 + if (i > 1) { + w += windingLine(xi, yi, x1, y1, x, y); + } + else { + // 第一个命令起点还未定义 + x0 = x1; + y0 = y1; + } + // zr 使用scale来模拟椭圆, 这里也对x做一定的缩放 + var _x = (x - cx) * ry / rx + cx; + if (isStroke) { + if (arc.containStroke( + cx, cy, ry, theta, theta + dTheta, anticlockwise, + lineWidth, _x, y + )) { + return true; + } + } + else { + w += windingArc( + cx, cy, ry, theta, theta + dTheta, anticlockwise, + _x, y + ); + } + xi = Math.cos(theta + dTheta) * rx + cx; + yi = Math.sin(theta + dTheta) * ry + cy; + break; + case CMD.R: + x0 = xi = data[i++]; + y0 = yi = data[i++]; + var width = data[i++]; + var height = data[i++]; + var x1 = x0 + width; + var y1 = y0 + height; + if (isStroke) { + if (containStroke(x0, y0, x1, y0, lineWidth, x, y) + || containStroke(x1, y0, x1, y1, lineWidth, x, y) + || containStroke(x1, y1, x0, y1, lineWidth, x, y) + || containStroke(x0, y1, x0, y0, lineWidth, x, y) + ) { + return true; + } + } + else { + // FIXME Clockwise ? + w += windingLine(x1, y0, x1, y1, x, y); + w += windingLine(x0, y1, x0, y0, x, y); + } + break; + case CMD.Z: + if (isStroke) { + if (containStroke( + xi, yi, x0, y0, lineWidth, x, y + )) { + return true; + } + } + else { + // Close a subpath + w += windingLine(xi, yi, x0, y0, x, y); + // 如果被任何一个 subpath 包含 + // FIXME subpaths may overlap + // if (w !== 0) { + // return true; + // } + } + xi = x0; + yi = y0; + break; + } + } + if (!isStroke && !isAroundEqual(yi, y0)) { + w += windingLine(xi, yi, x0, y0, x, y) || 0; + } + return w !== 0; + } + + module.exports = { + contain: function (pathData, x, y) { + return containPath(pathData, 0, false, x, y); + }, + + containStroke: function (pathData, lineWidth, x, y) { + return containPath(pathData, lineWidth, true, x, y); + } + }; + + +/***/ }, +/* 53 */ +/***/ function(module, exports) { + + + module.exports = { + /** + * 线段包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function (x0, y0, x1, y1, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + var _a = 0; + var _b = x0; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l) + || (y < y0 - _l && y < y1 - _l) + || (x > x0 + _l && x > x1 + _l) + || (x < x0 - _l && x < x1 - _l) + ) { + return false; + } + + if (x0 !== x1) { + _a = (y0 - y1) / (x0 - x1); + _b = (x0 * y1 - x1 * y0) / (x0 - x1) ; + } + else { + return Math.abs(x - x0) <= _l / 2; + } + var tmp = _a * x - y + _b; + var _s = tmp * tmp / (_a * _a + 1); + return _s <= _l / 2 * _l / 2; + } + }; + + +/***/ }, +/* 54 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curve = __webpack_require__(50); + + module.exports = { + /** + * 三次贝塞尔曲线描边包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} x3 + * @param {number} y3 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) + ) { + return false; + } + var d = curve.cubicProjectPoint( + x0, y0, x1, y1, x2, y2, x3, y3, + x, y, null + ); + return d <= _l / 2; + } + }; + + +/***/ }, +/* 55 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curve = __webpack_require__(50); + + module.exports = { + /** + * 二次贝塞尔曲线描边包含判断 + * @param {number} x0 + * @param {number} y0 + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {boolean} + */ + containStroke: function (x0, y0, x1, y1, x2, y2, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + // Quick reject + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l) + || (y < y0 - _l && y < y1 - _l && y < y2 - _l) + || (x > x0 + _l && x > x1 + _l && x > x2 + _l) + || (x < x0 - _l && x < x1 - _l && x < x2 - _l) + ) { + return false; + } + var d = curve.quadraticProjectPoint( + x0, y0, x1, y1, x2, y2, + x, y, null + ); + return d <= _l / 2; + } + }; + + +/***/ }, +/* 56 */ +/***/ function(module, exports, __webpack_require__) { + + + + var normalizeRadian = __webpack_require__(57).normalizeRadian; + var PI2 = Math.PI * 2; + + module.exports = { + /** + * 圆弧描边包含判断 + * @param {number} cx + * @param {number} cy + * @param {number} r + * @param {number} startAngle + * @param {number} endAngle + * @param {boolean} anticlockwise + * @param {number} lineWidth + * @param {number} x + * @param {number} y + * @return {Boolean} + */ + containStroke: function ( + cx, cy, r, startAngle, endAngle, anticlockwise, + lineWidth, x, y + ) { + + if (lineWidth === 0) { + return false; + } + var _l = lineWidth; + + x -= cx; + y -= cy; + var d = Math.sqrt(x * x + y * y); + + if ((d - _l > r) || (d + _l < r)) { + return false; + } + if (Math.abs(startAngle - endAngle) % PI2 < 1e-4) { + // Is a circle + return true; + } + if (anticlockwise) { + var tmp = startAngle; + startAngle = normalizeRadian(endAngle); + endAngle = normalizeRadian(tmp); + } else { + startAngle = normalizeRadian(startAngle); + endAngle = normalizeRadian(endAngle); + } + if (startAngle > endAngle) { + endAngle += PI2; + } + + var angle = Math.atan2(y, x); + if (angle < 0) { + angle += PI2; + } + return (angle >= startAngle && angle <= endAngle) + || (angle + PI2 >= startAngle && angle + PI2 <= endAngle); + } + }; + + +/***/ }, +/* 57 */ +/***/ function(module, exports) { + + + + var PI2 = Math.PI * 2; + module.exports = { + normalizeRadian: function(angle) { + angle %= PI2; + if (angle < 0) { + angle += PI2; + } + return angle; + } + }; + + +/***/ }, +/* 58 */ +/***/ function(module, exports) { + + + module.exports = function windingLine(x0, y0, x1, y1, x, y) { + if ((y > y0 && y > y1) || (y < y0 && y < y1)) { + return 0; + } + // Ignore horizontal line + if (y1 === y0) { + return 0; + } + var dir = y1 < y0 ? 1 : -1; + var t = (y - y0) / (y1 - y0); + + // Avoid winding error when intersection point is the connect point of two line of polygon + if (t === 1 || t === 0) { + dir = y1 < y0 ? 0.5 : -0.5; + } + + var x_ = t * (x1 - x0) + x0; + + return x_ > x ? dir : 0; + }; + + +/***/ }, +/* 59 */ +/***/ function(module, exports) { + + + + var Pattern = function (image, repeat) { + this.image = image; + this.repeat = repeat; + + // Can be cloned + this.type = 'pattern'; + }; + + Pattern.prototype.getCanvasPattern = function (ctx) { + + return this._canvasPattern + || (this._canvasPattern = ctx.createPattern(this.image, this.repeat)); + }; + + module.exports = Pattern; + + +/***/ }, +/* 60 */ +/***/ function(module, exports, __webpack_require__) { + + + + var CMD = __webpack_require__(49).CMD; + var vec2 = __webpack_require__(10); + var v2ApplyTransform = vec2.applyTransform; + + var points = [[], [], []]; + var mathSqrt = Math.sqrt; + var mathAtan2 = Math.atan2; + function transformPath(path, m) { + var data = path.data; + var cmd; + var nPoint; + var i; + var j; + var k; + var p; + + var M = CMD.M; + var C = CMD.C; + var L = CMD.L; + var R = CMD.R; + var A = CMD.A; + var Q = CMD.Q; + + for (i = 0, j = 0; i < data.length;) { + cmd = data[i++]; + j = i; + nPoint = 0; + + switch (cmd) { + case M: + nPoint = 1; + break; + case L: + nPoint = 1; + break; + case C: + nPoint = 3; + break; + case Q: + nPoint = 2; + break; + case A: + var x = m[4]; + var y = m[5]; + var sx = mathSqrt(m[0] * m[0] + m[1] * m[1]); + var sy = mathSqrt(m[2] * m[2] + m[3] * m[3]); + var angle = mathAtan2(-m[1] / sy, m[0] / sx); + // cx + data[i++] += x; + // cy + data[i++] += y; + // Scale rx and ry + // FIXME Assume psi is 0 here + data[i++] *= sx; + data[i++] *= sy; + + // Start angle + data[i++] += angle; + // end angle + data[i++] += angle; + // FIXME psi + i += 2; + j = i; + break; + case R: + // x0, y0 + p[0] = data[i++]; + p[1] = data[i++]; + v2ApplyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + // x1, y1 + p[0] += data[i++]; + p[1] += data[i++]; + v2ApplyTransform(p, p, m); + data[j++] = p[0]; + data[j++] = p[1]; + } + + for (k = 0; k < nPoint; k++) { + var p = points[k]; + p[0] = data[i++]; + p[1] = data[i++]; + + v2ApplyTransform(p, p, m); + // Write back + data[j++] = p[0]; + data[j++] = p[1]; + } + } + } + + module.exports = transformPath; + + +/***/ }, +/* 61 */ +/***/ function(module, exports) { + + + + /** + * @param {Array.} colorStops + */ + var Gradient = function (colorStops) { + + this.colorStops = colorStops || []; + }; + + Gradient.prototype = { + + constructor: Gradient, + + addColorStop: function (offset, color) { + this.colorStops.push({ + + offset: offset, + + color: color + }); + } + }; + + module.exports = Gradient; + + +/***/ }, +/* 62 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Image element + * @module zrender/graphic/Image + */ + + + + var Displayable = __webpack_require__(46); + var BoundingRect = __webpack_require__(9); + var zrUtil = __webpack_require__(4); + + var LRU = __webpack_require__(63); + var globalImageCache = new LRU(50); + /** + * @alias zrender/graphic/Image + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + function ZImage(opts) { + Displayable.call(this, opts); + } + + ZImage.prototype = { + + constructor: ZImage, + + type: 'image', + + brush: function (ctx, prevEl) { + var style = this.style; + var src = style.image; + var image; + + // Must bind each time + style.bind(ctx, this, prevEl); + // style.image is a url string + if (typeof src === 'string') { + image = this._image; + } + // style.image is an HTMLImageElement or HTMLCanvasElement or Canvas + else { + image = src; + } + // FIXME Case create many images with src + if (!image && src) { + // Try get from global image cache + var cachedImgObj = globalImageCache.get(src); + if (!cachedImgObj) { + // Create a new image + image = new Image(); + image.onload = function () { + image.onload = null; + for (var i = 0; i < cachedImgObj.pending.length; i++) { + cachedImgObj.pending[i].dirty(); + } + }; + cachedImgObj = { + image: image, + pending: [this] + }; + image.src = src; + globalImageCache.put(src, cachedImgObj); + this._image = image; + return; + } + else { + image = cachedImgObj.image; + this._image = image; + // Image is not complete finish, add to pending list + if (!image.width || !image.height) { + cachedImgObj.pending.push(this); + return; + } + } + } + + if (image) { + // 图片已经加载完成 + // if (image.nodeName.toUpperCase() == 'IMG') { + // if (!image.complete) { + // return; + // } + // } + // Else is canvas + + var width = style.width || image.width; + var height = style.height || image.height; + var x = style.x || 0; + var y = style.y || 0; + // 图片加载失败 + if (!image.width || !image.height) { + return; + } + + // 设置transform + this.setTransform(ctx); + + + if (style.sWidth && style.sHeight) { + var sx = style.sx || 0; + var sy = style.sy || 0; + ctx.drawImage( + image, + sx, sy, style.sWidth, style.sHeight, + x, y, width, height + ); + } + else if (style.sx && style.sy) { + var sx = style.sx; + var sy = style.sy; + var sWidth = width - sx; + var sHeight = height - sy; + ctx.drawImage( + image, + sx, sy, sWidth, sHeight, + x, y, width, height + ); + } + else { + ctx.drawImage(image, x, y, width, height); + } + + // 如果没设置宽和高的话自动根据图片宽高设置 + if (style.width == null) { + style.width = width; + } + if (style.height == null) { + style.height = height; + } + + this.restoreTransform(ctx); + + // Draw rect text + if (style.text != null) { + this.drawRectText(ctx, this.getBoundingRect()); + } + + } + }, + + getBoundingRect: function () { + var style = this.style; + if (! this._rect) { + this._rect = new BoundingRect( + style.x || 0, style.y || 0, style.width || 0, style.height || 0 + ); + } + return this._rect; + } + }; + + zrUtil.inherits(ZImage, Displayable); + + module.exports = ZImage; + + +/***/ }, +/* 63 */ +/***/ function(module, exports) { + + // Simple LRU cache use doubly linked list + // @module zrender/core/LRU + + + /** + * Simple double linked list. Compared with array, it has O(1) remove operation. + * @constructor + */ + var LinkedList = function() { + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.head = null; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.tail = null; + + this._len = 0; + }; + + var linkedListProto = LinkedList.prototype; + /** + * Insert a new value at the tail + * @param {} val + * @return {module:zrender/core/LRU~Entry} + */ + linkedListProto.insert = function(val) { + var entry = new Entry(val); + this.insertEntry(entry); + return entry; + }; + + /** + * Insert an entry at the tail + * @param {module:zrender/core/LRU~Entry} entry + */ + linkedListProto.insertEntry = function(entry) { + if (!this.head) { + this.head = this.tail = entry; + } + else { + this.tail.next = entry; + entry.prev = this.tail; + this.tail = entry; + } + this._len++; + }; + + /** + * Remove entry. + * @param {module:zrender/core/LRU~Entry} entry + */ + linkedListProto.remove = function(entry) { + var prev = entry.prev; + var next = entry.next; + if (prev) { + prev.next = next; + } + else { + // Is head + this.head = next; + } + if (next) { + next.prev = prev; + } + else { + // Is tail + this.tail = prev; + } + entry.next = entry.prev = null; + this._len--; + }; + + /** + * @return {number} + */ + linkedListProto.len = function() { + return this._len; + }; + + /** + * @constructor + * @param {} val + */ + var Entry = function(val) { + /** + * @type {} + */ + this.value = val; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.next; + + /** + * @type {module:zrender/core/LRU~Entry} + */ + this.prev; + }; + + /** + * LRU Cache + * @constructor + * @alias module:zrender/core/LRU + */ + var LRU = function(maxSize) { + + this._list = new LinkedList(); + + this._map = {}; + + this._maxSize = maxSize || 10; + }; + + var LRUProto = LRU.prototype; + + /** + * @param {string} key + * @param {} value + */ + LRUProto.put = function(key, value) { + var list = this._list; + var map = this._map; + if (map[key] == null) { + var len = list.len(); + if (len >= this._maxSize && len > 0) { + // Remove the least recently used + var leastUsedEntry = list.head; + list.remove(leastUsedEntry); + delete map[leastUsedEntry.key]; + } + + var entry = list.insert(value); + entry.key = key; + map[key] = entry; + } + }; + + /** + * @param {string} key + * @return {} + */ + LRUProto.get = function(key) { + var entry = this._map[key]; + var list = this._list; + if (entry != null) { + // Put the latest used entry in the tail + if (entry !== list.tail) { + list.remove(entry); + list.insertEntry(entry); + } + + return entry.value; + } + }; + + /** + * Clear the cache + */ + LRUProto.clear = function() { + this._list.clear(); + this._map = {}; + }; + + module.exports = LRU; + + +/***/ }, +/* 64 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Text element + * @module zrender/graphic/Text + * + * TODO Wrapping + * + * Text not support gradient + */ + + + + var Displayable = __webpack_require__(46); + var zrUtil = __webpack_require__(4); + var textContain = __webpack_require__(8); + + /** + * @alias zrender/graphic/Text + * @extends module:zrender/graphic/Displayable + * @constructor + * @param {Object} opts + */ + var Text = function (opts) { + Displayable.call(this, opts); + }; + + Text.prototype = { + + constructor: Text, + + type: 'text', + + brush: function (ctx, prevEl) { + var style = this.style; + var x = style.x || 0; + var y = style.y || 0; + // Convert to string + var text = style.text; + + // Convert to string + text != null && (text += ''); + + // Always bind style + style.bind(ctx, this, prevEl); + + if (text) { + + this.setTransform(ctx); + + var textBaseline; + var textAlign = style.textAlign; + var font = style.textFont || style.font; + if (style.textVerticalAlign) { + var rect = textContain.getBoundingRect( + text, font, style.textAlign, 'top' + ); + // Ignore textBaseline + textBaseline = 'middle'; + switch (style.textVerticalAlign) { + case 'middle': + y -= rect.height / 2 - rect.lineHeight / 2; + break; + case 'bottom': + y -= rect.height - rect.lineHeight / 2; + break; + default: + y += rect.lineHeight / 2; + } + } + else { + textBaseline = style.textBaseline; + } + + // TODO Invalid font + ctx.font = font || '12px sans-serif'; + ctx.textAlign = textAlign || 'left'; + // Use canvas default left textAlign. Giving invalid value will cause state not change + if (ctx.textAlign !== textAlign) { + ctx.textAlign = 'left'; + } + ctx.textBaseline = textBaseline || 'alphabetic'; + // Use canvas default alphabetic baseline + if (ctx.textBaseline !== textBaseline) { + ctx.textBaseline = 'alphabetic'; + } + + var lineHeight = textContain.measureText('国', ctx.font).width; + + var textLines = text.split('\n'); + for (var i = 0; i < textLines.length; i++) { + style.hasFill() && ctx.fillText(textLines[i], x, y); + style.hasStroke() && ctx.strokeText(textLines[i], x, y); + y += lineHeight; + } + + this.restoreTransform(ctx); + } + }, + + getBoundingRect: function () { + if (!this._rect) { + var style = this.style; + var textVerticalAlign = style.textVerticalAlign; + var rect = textContain.getBoundingRect( + style.text + '', style.textFont || style.font, style.textAlign, + textVerticalAlign ? 'top' : style.textBaseline + ); + switch (textVerticalAlign) { + case 'middle': + rect.y -= rect.height / 2; + break; + case 'bottom': + rect.y -= rect.height; + break; + } + rect.x += style.x || 0; + rect.y += style.y || 0; + this._rect = rect; + } + return this._rect; + } + }; + + zrUtil.inherits(Text, Displayable); + + module.exports = Text; + + +/***/ }, +/* 65 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 圆形 + * @module zrender/shape/Circle + */ + + + + module.exports = __webpack_require__(45).extend({ + + type: 'circle', + + shape: { + cx: 0, + cy: 0, + r: 0 + }, + + + buildPath : function (ctx, shape, inBundle) { + // Better stroking in ShapeBundle + // Always do it may have performence issue ( fill may be 2x more cost) + if (inBundle) { + ctx.moveTo(shape.cx + shape.r, shape.cy); + } + // Better stroking in ShapeBundle + // ctx.moveTo(shape.cx + shape.r, shape.cy); + ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2, true); + } + }); + + + +/***/ }, +/* 66 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 扇形 + * @module zrender/graphic/shape/Sector + */ + + + + module.exports = __webpack_require__(45).extend({ + + type: 'sector', + + shape: { + + cx: 0, + + cy: 0, + + r0: 0, + + r: 0, + + startAngle: 0, + + endAngle: Math.PI * 2, + + clockwise: true + }, + + buildPath: function (ctx, shape) { + + var x = shape.cx; + var y = shape.cy; + var r0 = Math.max(shape.r0 || 0, 0); + var r = Math.max(shape.r, 0); + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + + var unitX = Math.cos(startAngle); + var unitY = Math.sin(startAngle); + + ctx.moveTo(unitX * r0 + x, unitY * r0 + y); + + ctx.lineTo(unitX * r + x, unitY * r + y); + + ctx.arc(x, y, r, startAngle, endAngle, !clockwise); + + ctx.lineTo( + Math.cos(endAngle) * r0 + x, + Math.sin(endAngle) * r0 + y + ); + + if (r0 !== 0) { + ctx.arc(x, y, r0, endAngle, startAngle, clockwise); + } + + ctx.closePath(); + } + }); + + + +/***/ }, +/* 67 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 圆环 + * @module zrender/graphic/shape/Ring + */ + + + module.exports = __webpack_require__(45).extend({ + + type: 'ring', + + shape: { + cx: 0, + cy: 0, + r: 0, + r0: 0 + }, + + buildPath: function (ctx, shape) { + var x = shape.cx; + var y = shape.cy; + var PI2 = Math.PI * 2; + ctx.moveTo(x + shape.r, y); + ctx.arc(x, y, shape.r, 0, PI2, false); + ctx.moveTo(x + shape.r0, y); + ctx.arc(x, y, shape.r0, 0, PI2, true); + } + }); + + + +/***/ }, +/* 68 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 多边形 + * @module zrender/shape/Polygon + */ + + + var polyHelper = __webpack_require__(69); + + module.exports = __webpack_require__(45).extend({ + + type: 'polygon', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + buildPath: function (ctx, shape) { + polyHelper.buildPath(ctx, shape, true); + } + }); + + +/***/ }, +/* 69 */ +/***/ function(module, exports, __webpack_require__) { + + + + var smoothSpline = __webpack_require__(70); + var smoothBezier = __webpack_require__(71); + + module.exports = { + buildPath: function (ctx, shape, closePath) { + var points = shape.points; + var smooth = shape.smooth; + if (points && points.length >= 2) { + if (smooth && smooth !== 'spline') { + var controlPoints = smoothBezier( + points, smooth, closePath, shape.smoothConstraint + ); + + ctx.moveTo(points[0][0], points[0][1]); + var len = points.length; + for (var i = 0; i < (closePath ? len : len - 1); i++) { + var cp1 = controlPoints[i * 2]; + var cp2 = controlPoints[i * 2 + 1]; + var p = points[(i + 1) % len]; + ctx.bezierCurveTo( + cp1[0], cp1[1], cp2[0], cp2[1], p[0], p[1] + ); + } + } + else { + if (smooth === 'spline') { + points = smoothSpline(points, closePath); + } + + ctx.moveTo(points[0][0], points[0][1]); + for (var i = 1, l = points.length; i < l; i++) { + ctx.lineTo(points[i][0], points[i][1]); + } + } + + closePath && ctx.closePath(); + } + } + }; + + +/***/ }, +/* 70 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Catmull-Rom spline 插值折线 + * @module zrender/shape/util/smoothSpline + * @author pissang (https://www.github.com/pissang) + * Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + */ + + var vec2 = __webpack_require__(10); + + /** + * @inner + */ + function interpolate(p0, p1, p2, p3, t, t2, t3) { + var v0 = (p2 - p0) * 0.5; + var v1 = (p3 - p1) * 0.5; + return (2 * (p1 - p2) + v0 + v1) * t3 + + (-3 * (p1 - p2) - 2 * v0 - v1) * t2 + + v0 * t + p1; + } + + /** + * @alias module:zrender/shape/util/smoothSpline + * @param {Array} points 线段顶点数组 + * @param {boolean} isLoop + * @return {Array} + */ + module.exports = function (points, isLoop) { + var len = points.length; + var ret = []; + + var distance = 0; + for (var i = 1; i < len; i++) { + distance += vec2.distance(points[i - 1], points[i]); + } + + var segs = distance / 2; + segs = segs < len ? len : segs; + for (var i = 0; i < segs; i++) { + var pos = i / (segs - 1) * (isLoop ? len : len - 1); + var idx = Math.floor(pos); + + var w = pos - idx; + + var p0; + var p1 = points[idx % len]; + var p2; + var p3; + if (!isLoop) { + p0 = points[idx === 0 ? idx : idx - 1]; + p2 = points[idx > len - 2 ? len - 1 : idx + 1]; + p3 = points[idx > len - 3 ? len - 1 : idx + 2]; + } + else { + p0 = points[(idx - 1 + len) % len]; + p2 = points[(idx + 1) % len]; + p3 = points[(idx + 2) % len]; + } + + var w2 = w * w; + var w3 = w * w2; + + ret.push([ + interpolate(p0[0], p1[0], p2[0], p3[0], w, w2, w3), + interpolate(p0[1], p1[1], p2[1], p3[1], w, w2, w3) + ]); + } + return ret; + }; + + + +/***/ }, +/* 71 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 贝塞尔平滑曲线 + * @module zrender/shape/util/smoothBezier + * @author pissang (https://www.github.com/pissang) + * Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + */ + + + var vec2 = __webpack_require__(10); + var v2Min = vec2.min; + var v2Max = vec2.max; + var v2Scale = vec2.scale; + var v2Distance = vec2.distance; + var v2Add = vec2.add; + + /** + * 贝塞尔平滑曲线 + * @alias module:zrender/shape/util/smoothBezier + * @param {Array} points 线段顶点数组 + * @param {number} smooth 平滑等级, 0-1 + * @param {boolean} isLoop + * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内 + * 比如 [[0, 0], [100, 100]], 这个包围盒会与 + * 整个折线的包围盒做一个并集用来约束控制点。 + * @param {Array} 计算出来的控制点数组 + */ + module.exports = function (points, smooth, isLoop, constraint) { + var cps = []; + + var v = []; + var v1 = []; + var v2 = []; + var prevPoint; + var nextPoint; + + var min, max; + if (constraint) { + min = [Infinity, Infinity]; + max = [-Infinity, -Infinity]; + for (var i = 0, len = points.length; i < len; i++) { + v2Min(min, min, points[i]); + v2Max(max, max, points[i]); + } + // 与指定的包围盒做并集 + v2Min(min, min, constraint[0]); + v2Max(max, max, constraint[1]); + } + + for (var i = 0, len = points.length; i < len; i++) { + var point = points[i]; + + if (isLoop) { + prevPoint = points[i ? i - 1 : len - 1]; + nextPoint = points[(i + 1) % len]; + } + else { + if (i === 0 || i === len - 1) { + cps.push(vec2.clone(points[i])); + continue; + } + else { + prevPoint = points[i - 1]; + nextPoint = points[i + 1]; + } + } + + vec2.sub(v, nextPoint, prevPoint); + + // use degree to scale the handle length + v2Scale(v, v, smooth); + + var d0 = v2Distance(point, prevPoint); + var d1 = v2Distance(point, nextPoint); + var sum = d0 + d1; + if (sum !== 0) { + d0 /= sum; + d1 /= sum; + } + + v2Scale(v1, v, -d0); + v2Scale(v2, v, d1); + var cp0 = v2Add([], point, v1); + var cp1 = v2Add([], point, v2); + if (constraint) { + v2Max(cp0, cp0, min); + v2Min(cp0, cp0, max); + v2Max(cp1, cp1, min); + v2Min(cp1, cp1, max); + } + cps.push(cp0); + cps.push(cp1); + } + + if (isLoop) { + cps.push(cps.shift()); + } + + return cps; + }; + + + +/***/ }, +/* 72 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module zrender/graphic/shape/Polyline + */ + + + var polyHelper = __webpack_require__(69); + + module.exports = __webpack_require__(45).extend({ + + type: 'polyline', + + shape: { + points: null, + + smooth: false, + + smoothConstraint: null + }, + + style: { + stroke: '#000', + + fill: null + }, + + buildPath: function (ctx, shape) { + polyHelper.buildPath(ctx, shape, false); + } + }); + + +/***/ }, +/* 73 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 矩形 + * @module zrender/graphic/shape/Rect + */ + + + var roundRectHelper = __webpack_require__(74); + + module.exports = __webpack_require__(45).extend({ + + type: 'rect', + + shape: { + // 左上、右上、右下、左下角的半径依次为r1、r2、r3、r4 + // r缩写为1 相当于 [1, 1, 1, 1] + // r缩写为[1] 相当于 [1, 1, 1, 1] + // r缩写为[1, 2] 相当于 [1, 2, 1, 2] + // r缩写为[1, 2, 3] 相当于 [1, 2, 3, 2] + r: 0, + + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (ctx, shape) { + var x = shape.x; + var y = shape.y; + var width = shape.width; + var height = shape.height; + if (!shape.r) { + ctx.rect(x, y, width, height); + } + else { + roundRectHelper.buildPath(ctx, shape); + } + ctx.closePath(); + return; + } + }); + + + +/***/ }, +/* 74 */ +/***/ function(module, exports) { + + + + module.exports = { + buildPath: function (ctx, shape) { + var x = shape.x; + var y = shape.y; + var width = shape.width; + var height = shape.height; + var r = shape.r; + var r1; + var r2; + var r3; + var r4; + + // Convert width and height to positive for better borderRadius + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + + if (typeof r === 'number') { + r1 = r2 = r3 = r4 = r; + } + else if (r instanceof Array) { + if (r.length === 1) { + r1 = r2 = r3 = r4 = r[0]; + } + else if (r.length === 2) { + r1 = r3 = r[0]; + r2 = r4 = r[1]; + } + else if (r.length === 3) { + r1 = r[0]; + r2 = r4 = r[1]; + r3 = r[2]; + } + else { + r1 = r[0]; + r2 = r[1]; + r3 = r[2]; + r4 = r[3]; + } + } + else { + r1 = r2 = r3 = r4 = 0; + } + + var total; + if (r1 + r2 > width) { + total = r1 + r2; + r1 *= width / total; + r2 *= width / total; + } + if (r3 + r4 > width) { + total = r3 + r4; + r3 *= width / total; + r4 *= width / total; + } + if (r2 + r3 > height) { + total = r2 + r3; + r2 *= height / total; + r3 *= height / total; + } + if (r1 + r4 > height) { + total = r1 + r4; + r1 *= height / total; + r4 *= height / total; + } + ctx.moveTo(x + r1, y); + ctx.lineTo(x + width - r2, y); + r2 !== 0 && ctx.quadraticCurveTo( + x + width, y, x + width, y + r2 + ); + ctx.lineTo(x + width, y + height - r3); + r3 !== 0 && ctx.quadraticCurveTo( + x + width, y + height, x + width - r3, y + height + ); + ctx.lineTo(x + r4, y + height); + r4 !== 0 && ctx.quadraticCurveTo( + x, y + height, x, y + height - r4 + ); + ctx.lineTo(x, y + r1); + r1 !== 0 && ctx.quadraticCurveTo(x, y, x + r1, y); + } + }; + + +/***/ }, +/* 75 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 直线 + * @module zrender/graphic/shape/Line + */ + + module.exports = __webpack_require__(45).extend({ + + type: 'line', + + shape: { + // Start point + x1: 0, + y1: 0, + // End point + x2: 0, + y2: 0, + + percent: 1 + }, + + style: { + stroke: '#000', + fill: null + }, + + buildPath: function (ctx, shape) { + var x1 = shape.x1; + var y1 = shape.y1; + var x2 = shape.x2; + var y2 = shape.y2; + var percent = shape.percent; + + if (percent === 0) { + return; + } + + ctx.moveTo(x1, y1); + + if (percent < 1) { + x2 = x1 * (1 - percent) + x2 * percent; + y2 = y1 * (1 - percent) + y2 * percent; + } + ctx.lineTo(x2, y2); + }, + + /** + * Get point at percent + * @param {number} percent + * @return {Array.} + */ + pointAt: function (p) { + var shape = this.shape; + return [ + shape.x1 * (1 - p) + shape.x2 * p, + shape.y1 * (1 - p) + shape.y2 * p + ]; + } + }); + + + +/***/ }, +/* 76 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 贝塞尔曲线 + * @module zrender/shape/BezierCurve + */ + + + var curveTool = __webpack_require__(50); + var vec2 = __webpack_require__(10); + var quadraticSubdivide = curveTool.quadraticSubdivide; + var cubicSubdivide = curveTool.cubicSubdivide; + var quadraticAt = curveTool.quadraticAt; + var cubicAt = curveTool.cubicAt; + var quadraticDerivativeAt = curveTool.quadraticDerivativeAt; + var cubicDerivativeAt = curveTool.cubicDerivativeAt; + + var out = []; + + function someVectorAt(shape, t, isTangent) { + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + if (cpx2 === null || cpy2 === null) { + return [ + (isTangent ? cubicDerivativeAt : cubicAt)(shape.x1, shape.cpx1, shape.cpx2, shape.x2, t), + (isTangent ? cubicDerivativeAt : cubicAt)(shape.y1, shape.cpy1, shape.cpy2, shape.y2, t) + ]; + } + else { + return [ + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.x1, shape.cpx1, shape.x2, t), + (isTangent ? quadraticDerivativeAt : quadraticAt)(shape.y1, shape.cpy1, shape.y2, t) + ]; + } + } + module.exports = __webpack_require__(45).extend({ + + type: 'bezier-curve', + + shape: { + x1: 0, + y1: 0, + x2: 0, + y2: 0, + cpx1: 0, + cpy1: 0, + // cpx2: 0, + // cpy2: 0 + + // Curve show percent, for animating + percent: 1 + }, + + style: { + stroke: '#000', + fill: null + }, + + buildPath: function (ctx, shape) { + var x1 = shape.x1; + var y1 = shape.y1; + var x2 = shape.x2; + var y2 = shape.y2; + var cpx1 = shape.cpx1; + var cpy1 = shape.cpy1; + var cpx2 = shape.cpx2; + var cpy2 = shape.cpy2; + var percent = shape.percent; + if (percent === 0) { + return; + } + + ctx.moveTo(x1, y1); + + if (cpx2 == null || cpy2 == null) { + if (percent < 1) { + quadraticSubdivide( + x1, cpx1, x2, percent, out + ); + cpx1 = out[1]; + x2 = out[2]; + quadraticSubdivide( + y1, cpy1, y2, percent, out + ); + cpy1 = out[1]; + y2 = out[2]; + } + + ctx.quadraticCurveTo( + cpx1, cpy1, + x2, y2 + ); + } + else { + if (percent < 1) { + cubicSubdivide( + x1, cpx1, cpx2, x2, percent, out + ); + cpx1 = out[1]; + cpx2 = out[2]; + x2 = out[3]; + cubicSubdivide( + y1, cpy1, cpy2, y2, percent, out + ); + cpy1 = out[1]; + cpy2 = out[2]; + y2 = out[3]; + } + ctx.bezierCurveTo( + cpx1, cpy1, + cpx2, cpy2, + x2, y2 + ); + } + }, + + /** + * Get point at percent + * @param {number} t + * @return {Array.} + */ + pointAt: function (t) { + return someVectorAt(this.shape, t, false); + }, + + /** + * Get tangent at percent + * @param {number} t + * @return {Array.} + */ + tangentAt: function (t) { + var p = someVectorAt(this.shape, t, true); + return vec2.normalize(p, p); + } + }); + + + +/***/ }, +/* 77 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * 圆弧 + * @module zrender/graphic/shape/Arc + */ + + + module.exports = __webpack_require__(45).extend({ + + type: 'arc', + + shape: { + + cx: 0, + + cy: 0, + + r: 0, + + startAngle: 0, + + endAngle: Math.PI * 2, + + clockwise: true + }, + + style: { + + stroke: '#000', + + fill: null + }, + + buildPath: function (ctx, shape) { + + var x = shape.cx; + var y = shape.cy; + var r = Math.max(shape.r, 0); + var startAngle = shape.startAngle; + var endAngle = shape.endAngle; + var clockwise = shape.clockwise; + + var unitX = Math.cos(startAngle); + var unitY = Math.sin(startAngle); + + ctx.moveTo(unitX * r + x, unitY * r + y); + ctx.arc(x, y, r, startAngle, endAngle, !clockwise); + } + }); + + +/***/ }, +/* 78 */ +/***/ function(module, exports, __webpack_require__) { + + // CompoundPath to improve performance + + + var Path = __webpack_require__(45); + module.exports = Path.extend({ + + type: 'compound', + + shape: { + + paths: null + }, + + _updatePathDirty: function () { + var dirtyPath = this.__dirtyPath; + var paths = this.shape.paths; + for (var i = 0; i < paths.length; i++) { + // Mark as dirty if any subpath is dirty + dirtyPath = dirtyPath || paths[i].__dirtyPath; + } + this.__dirtyPath = dirtyPath; + this.__dirty = this.__dirty || dirtyPath; + }, + + beforeBrush: function () { + this._updatePathDirty(); + var paths = this.shape.paths || []; + var scale = this.getGlobalScale(); + // Update path scale + for (var i = 0; i < paths.length; i++) { + paths[i].path.setScale(scale[0], scale[1]); + } + }, + + buildPath: function (ctx, shape) { + var paths = shape.paths || []; + for (var i = 0; i < paths.length; i++) { + paths[i].buildPath(ctx, paths[i].shape, true); + } + }, + + afterBrush: function () { + var paths = this.shape.paths; + for (var i = 0; i < paths.length; i++) { + paths[i].__dirtyPath = false; + } + }, + + getBoundingRect: function () { + this._updatePathDirty(); + return Path.prototype.getBoundingRect.call(this); + } + }); + + +/***/ }, +/* 79 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var Gradient = __webpack_require__(61); + + /** + * x, y, x2, y2 are all percent from 0 to 1 + * @param {number} [x=0] + * @param {number} [y=0] + * @param {number} [x2=1] + * @param {number} [y2=0] + * @param {Array.} colorStops + * @param {boolean} [globalCoord=false] + */ + var LinearGradient = function (x, y, x2, y2, colorStops, globalCoord) { + this.x = x == null ? 0 : x; + + this.y = y == null ? 0 : y; + + this.x2 = x2 == null ? 1 : x2; + + this.y2 = y2 == null ? 0 : y2; + + // Can be cloned + this.type = 'linear'; + + // If use global coord + this.global = globalCoord || false; + + Gradient.call(this, colorStops); + }; + + LinearGradient.prototype = { + + constructor: LinearGradient + }; + + zrUtil.inherits(LinearGradient, Gradient); + + module.exports = LinearGradient; + + +/***/ }, +/* 80 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + var Gradient = __webpack_require__(61); + + /** + * x, y, r are all percent from 0 to 1 + * @param {number} [x=0.5] + * @param {number} [y=0.5] + * @param {number} [r=0.5] + * @param {Array.} [colorStops] + * @param {boolean} [globalCoord=false] + */ + var RadialGradient = function (x, y, r, colorStops, globalCoord) { + this.x = x == null ? 0.5 : x; + + this.y = y == null ? 0.5 : y; + + this.r = r == null ? 0.5 : r; + + // Can be cloned + this.type = 'radial'; + + // If use global coord + this.global = globalCoord || false; + + Gradient.call(this, colorStops); + }; + + RadialGradient.prototype = { + + constructor: RadialGradient + }; + + zrUtil.inherits(RadialGradient, Gradient); + + module.exports = RadialGradient; + + +/***/ }, +/* 81 */ +/***/ function(module, exports, __webpack_require__) { + + /*! + * ZRender, a high performance 2d drawing library. + * + * Copyright (c) 2013, Baidu Inc. + * All rights reserved. + * + * LICENSE + * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt + */ + // Global defines + + var guid = __webpack_require__(32); + var env = __webpack_require__(2); + + var Handler = __webpack_require__(82); + var Storage = __webpack_require__(84); + var Animation = __webpack_require__(86); + var HandlerProxy = __webpack_require__(89); + + var useVML = !env.canvasSupported; + + var painterCtors = { + canvas: __webpack_require__(91) + }; + + var instances = {}; // ZRender实例map索引 + + var zrender = {}; + /** + * @type {string} + */ + zrender.version = '3.1.3'; + + /** + * Initializing a zrender instance + * @param {HTMLElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + * @return {module:zrender/ZRender} + */ + zrender.init = function(dom, opts) { + var zr = new ZRender(guid(), dom, opts); + instances[zr.id] = zr; + return zr; + }; + + /** + * Dispose zrender instance + * @param {module:zrender/ZRender} zr + */ + zrender.dispose = function (zr) { + if (zr) { + zr.dispose(); + } + else { + for (var key in instances) { + instances[key].dispose(); + } + instances = {}; + } + + return zrender; + }; + + /** + * Get zrender instance by id + * @param {string} id zrender instance id + * @return {module:zrender/ZRender} + */ + zrender.getInstance = function (id) { + return instances[id]; + }; + + zrender.registerPainter = function (name, Ctor) { + painterCtors[name] = Ctor; + }; + + function delInstance(id) { + delete instances[id]; + } + + /** + * @module zrender/ZRender + */ + /** + * @constructor + * @alias module:zrender/ZRender + * @param {string} id + * @param {HTMLDomElement} dom + * @param {Object} opts + * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg' + * @param {number} [opts.devicePixelRatio] + */ + var ZRender = function(id, dom, opts) { + + opts = opts || {}; + + /** + * @type {HTMLDomElement} + */ + this.dom = dom; + + /** + * @type {string} + */ + this.id = id; + + var self = this; + var storage = new Storage(); + + var rendererType = opts.renderer; + if (useVML) { + if (!painterCtors.vml) { + throw new Error('You need to require \'zrender/vml/vml\' to support IE8'); + } + rendererType = 'vml'; + } + else if (!rendererType || !painterCtors[rendererType]) { + rendererType = 'canvas'; + } + var painter = new painterCtors[rendererType](dom, storage, opts); + + this.storage = storage; + this.painter = painter; + + var handerProxy = !env.node ? new HandlerProxy(painter.getViewportRoot()) : null; + this.handler = new Handler(storage, painter, handerProxy); + + /** + * @type {module:zrender/animation/Animation} + */ + this.animation = new Animation({ + stage: { + update: function () { + if (self._needsRefresh) { + self.refreshImmediately(); + } + if (self._needsRefreshHover) { + self.refreshHoverImmediately(); + } + } + } + }); + this.animation.start(); + + /** + * @type {boolean} + * @private + */ + this._needsRefresh; + + // 修改 storage.delFromMap, 每次删除元素之前删除动画 + // FIXME 有点ugly + var oldDelFromMap = storage.delFromMap; + var oldAddToMap = storage.addToMap; + + storage.delFromMap = function (elId) { + var el = storage.get(elId); + + oldDelFromMap.call(storage, elId); + + el && el.removeSelfFromZr(self); + }; + + storage.addToMap = function (el) { + oldAddToMap.call(storage, el); + + el.addSelfToZr(self); + }; + }; + + ZRender.prototype = { + + constructor: ZRender, + /** + * 获取实例唯一标识 + * @return {string} + */ + getId: function () { + return this.id; + }, + + /** + * 添加元素 + * @param {module:zrender/Element} el + */ + add: function (el) { + this.storage.addRoot(el); + this._needsRefresh = true; + }, + + /** + * 删除元素 + * @param {module:zrender/Element} el + */ + remove: function (el) { + this.storage.delRoot(el); + this._needsRefresh = true; + }, + + /** + * Change configuration of layer + * @param {string} zLevel + * @param {Object} config + * @param {string} [config.clearColor=0] Clear color + * @param {string} [config.motionBlur=false] If enable motion blur + * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer + */ + configLayer: function (zLevel, config) { + this.painter.configLayer(zLevel, config); + this._needsRefresh = true; + }, + + /** + * Repaint the canvas immediately + */ + refreshImmediately: function () { + // Clear needsRefresh ahead to avoid something wrong happens in refresh + // Or it will cause zrender refreshes again and again. + this._needsRefresh = false; + this.painter.refresh(); + /** + * Avoid trigger zr.refresh in Element#beforeUpdate hook + */ + this._needsRefresh = false; + }, + + /** + * Mark and repaint the canvas in the next frame of browser + */ + refresh: function() { + this._needsRefresh = true; + }, + + /** + * Add element to hover layer + * @param {module:zrender/Element} el + * @param {Object} style + */ + addHover: function (el, style) { + if (this.painter.addHover) { + this.painter.addHover(el, style); + this.refreshHover(); + } + }, + + /** + * Add element from hover layer + * @param {module:zrender/Element} el + */ + removeHover: function (el) { + if (this.painter.removeHover) { + this.painter.removeHover(el); + this.refreshHover(); + } + }, + + /** + * Clear all hover elements in hover layer + * @param {module:zrender/Element} el + */ + clearHover: function () { + if (this.painter.clearHover) { + this.painter.clearHover(); + this.refreshHover(); + } + }, + + /** + * Refresh hover in next frame + */ + refreshHover: function () { + this._needsRefreshHover = true; + }, + + /** + * Refresh hover immediately + */ + refreshHoverImmediately: function () { + this._needsRefreshHover = false; + this.painter.refreshHover && this.painter.refreshHover(); + }, + + /** + * Resize the canvas. + * Should be invoked when container size is changed + */ + resize: function() { + this.painter.resize(); + this.handler.resize(); + }, + + /** + * Stop and clear all animation immediately + */ + clearAnimation: function () { + this.animation.clear(); + }, + + /** + * Get container width + */ + getWidth: function() { + return this.painter.getWidth(); + }, + + /** + * Get container height + */ + getHeight: function() { + return this.painter.getHeight(); + }, + + /** + * Export the canvas as Base64 URL + * @param {string} type + * @param {string} [backgroundColor='#fff'] + * @return {string} Base64 URL + */ + // toDataURL: function(type, backgroundColor) { + // return this.painter.getRenderedCanvas({ + // backgroundColor: backgroundColor + // }).toDataURL(type); + // }, + + /** + * Converting a path to image. + * It has much better performance of drawing image rather than drawing a vector path. + * @param {module:zrender/graphic/Path} e + * @param {number} width + * @param {number} height + */ + pathToImage: function(e, width, height) { + var id = guid(); + return this.painter.pathToImage(id, e, width, height); + }, + + /** + * Set default cursor + * @param {string} [cursorStyle='default'] 例如 crosshair + */ + setCursorStyle: function (cursorStyle) { + this.handler.setCursorStyle(cursorStyle); + }, + + /** + * Bind event + * + * @param {string} eventName Event name + * @param {Function} eventHandler Handler function + * @param {Object} [context] Context object + */ + on: function(eventName, eventHandler, context) { + this.handler.on(eventName, eventHandler, context); + }, + + /** + * Unbind event + * @param {string} eventName Event name + * @param {Function} [eventHandler] Handler function + */ + off: function(eventName, eventHandler) { + this.handler.off(eventName, eventHandler); + }, + + /** + * Trigger event manually + * + * @param {string} eventName Event name + * @param {event=} event Event object + */ + trigger: function (eventName, event) { + this.handler.trigger(eventName, event); + }, + + + /** + * Clear all objects and the canvas. + */ + clear: function () { + this.storage.delRoot(); + this.painter.clear(); + }, + + /** + * Dispose self. + */ + dispose: function () { + this.animation.stop(); + + this.clear(); + this.storage.dispose(); + this.painter.dispose(); + this.handler.dispose(); + + this.animation = + this.storage = + this.painter = + this.handler = null; + + delInstance(this.id); + } + }; + + module.exports = zrender; + + + +/***/ }, +/* 82 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Handler + * @module zrender/Handler + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + * pissang (shenyi.914@gmail.com) + */ + + + var util = __webpack_require__(4); + var Draggable = __webpack_require__(83); + + var Eventful = __webpack_require__(33); + + function makeEventPacket(eveType, target, event) { + return { + type: eveType, + event: event, + target: target, + cancelBubble: false, + offsetX: event.zrX, + offsetY: event.zrY, + gestureEvent: event.gestureEvent, + pinchX: event.pinchX, + pinchY: event.pinchY, + pinchScale: event.pinchScale, + wheelDelta: event.zrDelta + }; + } + + function EmptyProxy () {} + EmptyProxy.prototype.dispose = function () {}; + + var handlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove' + ]; + /** + * @alias module:zrender/Handler + * @constructor + * @extends module:zrender/mixin/Eventful + * @param {HTMLElement} root Main HTML element for painting. + * @param {module:zrender/Storage} storage Storage instance. + * @param {module:zrender/Painter} painter Painter instance. + */ + var Handler = function(storage, painter, proxy) { + Eventful.call(this); + + this.storage = storage; + + this.painter = painter; + + proxy = proxy || new EmptyProxy(); + /** + * Proxy of event. can be Dom, WebGLSurface, etc. + */ + this.proxy = proxy; + + // Attach handler + proxy.handler = this; + + /** + * @private + * @type {boolean} + */ + this._hovered; + + /** + * @private + * @type {Date} + */ + this._lastTouchMoment; + + /** + * @private + * @type {number} + */ + this._lastX; + + /** + * @private + * @type {number} + */ + this._lastY; + + + Draggable.call(this); + + util.each(handlerNames, function (name) { + proxy.on && proxy.on(name, this[name], this); + }, this); + }; + + Handler.prototype = { + + constructor: Handler, + + mousemove: function (event) { + var x = event.zrX; + var y = event.zrY; + + var hovered = this.findHover(x, y, null); + var lastHovered = this._hovered; + var proxy = this.proxy; + + this._hovered = hovered; + + proxy.setCursor && proxy.setCursor(hovered ? hovered.cursor : 'default'); + + // Mouse out on previous hovered element + if (lastHovered && hovered !== lastHovered && lastHovered.__zr) { + this.dispatchToElement(lastHovered, 'mouseout', event); + } + + // Mouse moving on one element + this.dispatchToElement(hovered, 'mousemove', event); + + // Mouse over on a new element + if (hovered && hovered !== lastHovered) { + this.dispatchToElement(hovered, 'mouseover', event); + } + }, + + mouseout: function (event) { + this.dispatchToElement(this._hovered, 'mouseout', event); + + this.trigger('globalout', { + event: event + }); + }, + + /** + * Resize + */ + resize: function (event) { + this._hovered = null; + }, + + /** + * Dispatch event + * @param {string} eventName + * @param {event=} eventArgs + */ + dispatch: function (eventName, eventArgs) { + var handler = this[eventName]; + handler && handler.call(this, eventArgs); + }, + + /** + * Dispose + */ + dispose: function () { + + this.proxy.dispose(); + + this.storage = + this.proxy = + this.painter = null; + }, + + /** + * 设置默认的cursor style + * @param {string} [cursorStyle='default'] 例如 crosshair + */ + setCursorStyle: function (cursorStyle) { + var proxy = this.proxy; + proxy.setCursor && proxy.setCursor(cursorStyle); + }, + + /** + * 事件分发代理 + * + * @private + * @param {Object} targetEl 目标图形元素 + * @param {string} eventName 事件名称 + * @param {Object} event 事件对象 + */ + dispatchToElement: function (targetEl, eventName, event) { + var eventHandler = 'on' + eventName; + var eventPacket = makeEventPacket(eventName, targetEl, event); + + var el = targetEl; + + while (el) { + el[eventHandler] + && (eventPacket.cancelBubble = el[eventHandler].call(el, eventPacket)); + + el.trigger(eventName, eventPacket); + + el = el.parent; + + if (eventPacket.cancelBubble) { + break; + } + } + + if (!eventPacket.cancelBubble) { + // 冒泡到顶级 zrender 对象 + this.trigger(eventName, eventPacket); + // 分发事件到用户自定义层 + // 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在 + this.painter && this.painter.eachOtherLayer(function (layer) { + if (typeof(layer[eventHandler]) == 'function') { + layer[eventHandler].call(layer, eventPacket); + } + if (layer.trigger) { + layer.trigger(eventName, eventPacket); + } + }); + } + }, + + /** + * @private + * @param {number} x + * @param {number} y + * @param {module:zrender/graphic/Displayable} exclude + * @method + */ + findHover: function(x, y, exclude) { + var list = this.storage.getDisplayList(); + for (var i = list.length - 1; i >= 0 ; i--) { + if (!list[i].silent + && list[i] !== exclude + // getDisplayList may include ignored item in VML mode + && !list[i].ignore + && isHover(list[i], x, y)) { + return list[i]; + } + } + } + }; + + // Common handlers + util.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick'], function (name) { + Handler.prototype[name] = function (event) { + // Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover + var hovered = this.findHover(event.zrX, event.zrY, null); + + if (name === 'mousedown') { + this._downel = hovered; + // In case click triggered before mouseup + this._upel = hovered; + } + else if (name === 'mosueup') { + this._upel = hovered; + } + else if (name === 'click') { + if (this._downel !== this._upel) { + return; + } + } + + this.dispatchToElement(hovered, name, event); + }; + }); + + function isHover(displayable, x, y) { + if (displayable[displayable.rectHover ? 'rectContain' : 'contain'](x, y)) { + var el = displayable; + while (el) { + // If ancestor is silent or clipped by ancestor + if (el.silent || (el.clipPath && !el.clipPath.contain(x, y))) { + return false; + } + el = el.parent; + } + return true; + } + + return false; + } + + util.mixin(Handler, Eventful); + util.mixin(Handler, Draggable); + + module.exports = Handler; + + +/***/ }, +/* 83 */ +/***/ function(module, exports) { + + // TODO Draggable for group + // FIXME Draggable on element which has parent rotation or scale + + function Draggable() { + + this.on('mousedown', this._dragStart, this); + this.on('mousemove', this._drag, this); + this.on('mouseup', this._dragEnd, this); + this.on('globalout', this._dragEnd, this); + // this._dropTarget = null; + // this._draggingTarget = null; + + // this._x = 0; + // this._y = 0; + } + + Draggable.prototype = { + + constructor: Draggable, + + _dragStart: function (e) { + var draggingTarget = e.target; + if (draggingTarget && draggingTarget.draggable) { + this._draggingTarget = draggingTarget; + draggingTarget.dragging = true; + this._x = e.offsetX; + this._y = e.offsetY; + + this.dispatchToElement(draggingTarget, 'dragstart', e.event); + } + }, + + _drag: function (e) { + var draggingTarget = this._draggingTarget; + if (draggingTarget) { + + var x = e.offsetX; + var y = e.offsetY; + + var dx = x - this._x; + var dy = y - this._y; + this._x = x; + this._y = y; + + draggingTarget.drift(dx, dy, e); + this.dispatchToElement(draggingTarget, 'drag', e.event); + + var dropTarget = this.findHover(x, y, draggingTarget); + var lastDropTarget = this._dropTarget; + this._dropTarget = dropTarget; + + if (draggingTarget !== dropTarget) { + if (lastDropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(lastDropTarget, 'dragleave', e.event); + } + if (dropTarget && dropTarget !== lastDropTarget) { + this.dispatchToElement(dropTarget, 'dragenter', e.event); + } + } + } + }, + + _dragEnd: function (e) { + var draggingTarget = this._draggingTarget; + + if (draggingTarget) { + draggingTarget.dragging = false; + } + + this.dispatchToElement(draggingTarget, 'dragend', e.event); + + if (this._dropTarget) { + this.dispatchToElement(this._dropTarget, 'drop', e.event); + } + + this._draggingTarget = null; + this._dropTarget = null; + } + + }; + + module.exports = Draggable; + + +/***/ }, +/* 84 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Storage内容仓库模块 + * @module zrender/Storage + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * @author errorrik (errorrik@gmail.com) + * @author pissang (https://github.com/pissang/) + */ + + + var util = __webpack_require__(4); + var env = __webpack_require__(2); + + var Group = __webpack_require__(30); + + // Use timsort because in most case elements are partially sorted + // https://jsfiddle.net/pissang/jr4x7mdm/8/ + var timsort = __webpack_require__(85); + + function shapeCompareFunc(a, b) { + if (a.zlevel === b.zlevel) { + if (a.z === b.z) { + // if (a.z2 === b.z2) { + // // FIXME Slow has renderidx compare + // // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement + // // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012 + // return a.__renderidx - b.__renderidx; + // } + return a.z2 - b.z2; + } + return a.z - b.z; + } + return a.zlevel - b.zlevel; + } + /** + * 内容仓库 (M) + * @alias module:zrender/Storage + * @constructor + */ + var Storage = function () { + // 所有常规形状,id索引的map + this._elements = {}; + + this._roots = []; + + this._displayList = []; + + this._displayListLen = 0; + }; + + Storage.prototype = { + + constructor: Storage, + + /** + * @param {Function} cb + * + */ + traverse: function (cb, context) { + for (var i = 0; i < this._roots.length; i++) { + this._roots[i].traverse(cb, context); + } + }, + + /** + * 返回所有图形的绘制队列 + * @param {boolean} [update=false] 是否在返回前更新该数组 + * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组, 在 update 为 true 的时候有效 + * + * 详见{@link module:zrender/graphic/Displayable.prototype.updateDisplayList} + * @return {Array.} + */ + getDisplayList: function (update, includeIgnore) { + includeIgnore = includeIgnore || false; + if (update) { + this.updateDisplayList(includeIgnore); + } + return this._displayList; + }, + + /** + * 更新图形的绘制队列。 + * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中, + * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列 + * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组 + */ + updateDisplayList: function (includeIgnore) { + this._displayListLen = 0; + var roots = this._roots; + var displayList = this._displayList; + for (var i = 0, len = roots.length; i < len; i++) { + this._updateAndAddDisplayable(roots[i], null, includeIgnore); + } + displayList.length = this._displayListLen; + + // for (var i = 0, len = displayList.length; i < len; i++) { + // displayList[i].__renderidx = i; + // } + + // displayList.sort(shapeCompareFunc); + env.canvasSupported && timsort(displayList, shapeCompareFunc); + }, + + _updateAndAddDisplayable: function (el, clipPaths, includeIgnore) { + + if (el.ignore && !includeIgnore) { + return; + } + + el.beforeUpdate(); + + if (el.__dirty) { + + el.update(); + + } + + el.afterUpdate(); + + var clipPath = el.clipPath; + if (clipPath) { + // clipPath 的变换是基于 group 的变换 + clipPath.parent = el; + clipPath.updateTransform(); + + // FIXME 效率影响 + if (clipPaths) { + clipPaths = clipPaths.slice(); + clipPaths.push(clipPath); + } + else { + clipPaths = [clipPath]; + } + } + + if (el.isGroup) { + var children = el._children; + + for (var i = 0; i < children.length; i++) { + var child = children[i]; + + // Force to mark as dirty if group is dirty + // FIXME __dirtyPath ? + if (el.__dirty) { + child.__dirty = true; + } + + this._updateAndAddDisplayable(child, clipPaths, includeIgnore); + } + + // Mark group clean here + el.__dirty = false; + + } + else { + el.__clipPaths = clipPaths; + + this._displayList[this._displayListLen++] = el; + } + }, + + /** + * 添加图形(Shape)或者组(Group)到根节点 + * @param {module:zrender/Element} el + */ + addRoot: function (el) { + // Element has been added + if (this._elements[el.id]) { + return; + } + + if (el instanceof Group) { + el.addChildrenToStorage(this); + } + + this.addToMap(el); + this._roots.push(el); + }, + + /** + * 删除指定的图形(Shape)或者组(Group) + * @param {string|Array.} [elId] 如果为空清空整个Storage + */ + delRoot: function (elId) { + if (elId == null) { + // 不指定elId清空 + for (var i = 0; i < this._roots.length; i++) { + var root = this._roots[i]; + if (root instanceof Group) { + root.delChildrenFromStorage(this); + } + } + + this._elements = {}; + this._roots = []; + this._displayList = []; + this._displayListLen = 0; + + return; + } + + if (elId instanceof Array) { + for (var i = 0, l = elId.length; i < l; i++) { + this.delRoot(elId[i]); + } + return; + } + + var el; + if (typeof(elId) == 'string') { + el = this._elements[elId]; + } + else { + el = elId; + } + + var idx = util.indexOf(this._roots, el); + if (idx >= 0) { + this.delFromMap(el.id); + this._roots.splice(idx, 1); + if (el instanceof Group) { + el.delChildrenFromStorage(this); + } + } + }, + + addToMap: function (el) { + if (el instanceof Group) { + el.__storage = this; + } + el.dirty(false); + + this._elements[el.id] = el; + + return this; + }, + + get: function (elId) { + return this._elements[elId]; + }, + + delFromMap: function (elId) { + var elements = this._elements; + var el = elements[elId]; + if (el) { + delete elements[elId]; + if (el instanceof Group) { + el.__storage = null; + } + } + + return this; + }, + + /** + * 清空并且释放Storage + */ + dispose: function () { + this._elements = + this._renderList = + this._roots = null; + }, + + displayableSortFunc: shapeCompareFunc + }; + + module.exports = Storage; + + + +/***/ }, +/* 85 */ +/***/ function(module, exports) { + + // https://github.com/mziccard/node-timsort + + var DEFAULT_MIN_MERGE = 32; + + var DEFAULT_MIN_GALLOPING = 7; + + var DEFAULT_TMP_STORAGE_LENGTH = 256; + + function minRunLength(n) { + var r = 0; + + while (n >= DEFAULT_MIN_MERGE) { + r |= n & 1; + n >>= 1; + } + + return n + r; + } + + function makeAscendingRun(array, lo, hi, compare) { + var runHi = lo + 1; + + if (runHi === hi) { + return 1; + } + + if (compare(array[runHi++], array[lo]) < 0) { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) { + runHi++; + } + + reverseRun(array, lo, runHi); + } + else { + while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { + runHi++; + } + } + + return runHi - lo; + } + + function reverseRun(array, lo, hi) { + hi--; + + while (lo < hi) { + var t = array[lo]; + array[lo++] = array[hi]; + array[hi--] = t; + } + } + + function binaryInsertionSort(array, lo, hi, start, compare) { + if (start === lo) { + start++; + } + + for (; start < hi; start++) { + var pivot = array[start]; + + var left = lo; + var right = start; + var mid; + + while (left < right) { + mid = left + right >>> 1; + + if (compare(pivot, array[mid]) < 0) { + right = mid; + } + else { + left = mid + 1; + } + } + + var n = start - left; + + switch (n) { + case 3: + array[left + 3] = array[left + 2]; + + case 2: + array[left + 2] = array[left + 1]; + + case 1: + array[left + 1] = array[left]; + break; + default: + while (n > 0) { + array[left + n] = array[left + n - 1]; + n--; + } + } + + array[left] = pivot; + } + } + + function gallopLeft(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + + if (compare(value, array[start + hint]) > 0) { + maxOffset = length - hint; + + while (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + lastOffset += hint; + offset += hint; + } + else { + maxOffset = hint + 1; + while (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + if (offset > maxOffset) { + offset = maxOffset; + } + + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + + lastOffset++; + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + + if (compare(value, array[start + m]) > 0) { + lastOffset = m + 1; + } + else { + offset = m; + } + } + return offset; + } + + function gallopRight(value, array, start, length, hint, compare) { + var lastOffset = 0; + var maxOffset = 0; + var offset = 1; + + if (compare(value, array[start + hint]) < 0) { + maxOffset = hint + 1; + + while (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + var tmp = lastOffset; + lastOffset = hint - offset; + offset = hint - tmp; + } + else { + maxOffset = length - hint; + + while (offset < maxOffset && compare(value, array[start + hint + offset]) >= 0) { + lastOffset = offset; + offset = (offset << 1) + 1; + + if (offset <= 0) { + offset = maxOffset; + } + } + + if (offset > maxOffset) { + offset = maxOffset; + } + + lastOffset += hint; + offset += hint; + } + + lastOffset++; + + while (lastOffset < offset) { + var m = lastOffset + (offset - lastOffset >>> 1); + + if (compare(value, array[start + m]) < 0) { + offset = m; + } + else { + lastOffset = m + 1; + } + } + + return offset; + } + + function TimSort(array, compare) { + var minGallop = DEFAULT_MIN_GALLOPING; + var length = 0; + var tmpStorageLength = DEFAULT_TMP_STORAGE_LENGTH; + var stackLength = 0; + var runStart; + var runLength; + var stackSize = 0; + + length = array.length; + + if (length < 2 * DEFAULT_TMP_STORAGE_LENGTH) { + tmpStorageLength = length >>> 1; + } + + var tmp = []; + + stackLength = length < 120 ? 5 : length < 1542 ? 10 : length < 119151 ? 19 : 40; + + runStart = []; + runLength = []; + + function pushRun(_runStart, _runLength) { + runStart[stackSize] = _runStart; + runLength[stackSize] = _runLength; + stackSize += 1; + } + + function mergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + + if (n >= 1 && runLength[n - 1] <= runLength[n] + runLength[n + 1] || n >= 2 && runLength[n - 2] <= runLength[n] + runLength[n - 1]) { + if (runLength[n - 1] < runLength[n + 1]) { + n--; + } + } + else if (runLength[n] > runLength[n + 1]) { + break; + } + mergeAt(n); + } + } + + function forceMergeRuns() { + while (stackSize > 1) { + var n = stackSize - 2; + + if (n > 0 && runLength[n - 1] < runLength[n + 1]) { + n--; + } + + mergeAt(n); + } + } + + function mergeAt(i) { + var start1 = runStart[i]; + var length1 = runLength[i]; + var start2 = runStart[i + 1]; + var length2 = runLength[i + 1]; + + runLength[i] = length1 + length2; + + if (i === stackSize - 3) { + runStart[i + 1] = runStart[i + 2]; + runLength[i + 1] = runLength[i + 2]; + } + + stackSize--; + + var k = gallopRight(array[start2], array, start1, length1, 0, compare); + start1 += k; + length1 -= k; + + if (length1 === 0) { + return; + } + + length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); + + if (length2 === 0) { + return; + } + + if (length1 <= length2) { + mergeLow(start1, length1, start2, length2); + } + else { + mergeHigh(start1, length1, start2, length2); + } + } + + function mergeLow(start1, length1, start2, length2) { + var i = 0; + + for (i = 0; i < length1; i++) { + tmp[i] = array[start1 + i]; + } + + var cursor1 = 0; + var cursor2 = start2; + var dest = start1; + + array[dest++] = array[cursor2++]; + + if (--length2 === 0) { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + return; + } + + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + return; + } + + var _minGallop = minGallop; + var count1, count2, exit; + + while (1) { + count1 = 0; + count2 = 0; + exit = false; + + do { + if (compare(array[cursor2], tmp[cursor1]) < 0) { + array[dest++] = array[cursor2++]; + count2++; + count1 = 0; + + if (--length2 === 0) { + exit = true; + break; + } + } + else { + array[dest++] = tmp[cursor1++]; + count1++; + count2 = 0; + if (--length1 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + + if (exit) { + break; + } + + do { + count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); + + if (count1 !== 0) { + for (i = 0; i < count1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + + dest += count1; + cursor1 += count1; + length1 -= count1; + if (length1 <= 1) { + exit = true; + break; + } + } + + array[dest++] = array[cursor2++]; + + if (--length2 === 0) { + exit = true; + break; + } + + count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); + + if (count2 !== 0) { + for (i = 0; i < count2; i++) { + array[dest + i] = array[cursor2 + i]; + } + + dest += count2; + cursor2 += count2; + length2 -= count2; + + if (length2 === 0) { + exit = true; + break; + } + } + array[dest++] = tmp[cursor1++]; + + if (--length1 === 1) { + exit = true; + break; + } + + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + + if (exit) { + break; + } + + if (_minGallop < 0) { + _minGallop = 0; + } + + _minGallop += 2; + } + + minGallop = _minGallop; + + minGallop < 1 && (minGallop = 1); + + if (length1 === 1) { + for (i = 0; i < length2; i++) { + array[dest + i] = array[cursor2 + i]; + } + array[dest + length2] = tmp[cursor1]; + } + else if (length1 === 0) { + throw new Error(); + // throw new Error('mergeLow preconditions were not respected'); + } + else { + for (i = 0; i < length1; i++) { + array[dest + i] = tmp[cursor1 + i]; + } + } + } + + function mergeHigh (start1, length1, start2, length2) { + var i = 0; + + for (i = 0; i < length2; i++) { + tmp[i] = array[start2 + i]; + } + + var cursor1 = start1 + length1 - 1; + var cursor2 = length2 - 1; + var dest = start2 + length2 - 1; + var customCursor = 0; + var customDest = 0; + + array[dest--] = array[cursor1--]; + + if (--length1 === 0) { + customCursor = dest - (length2 - 1); + + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + + return; + } + + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + array[dest] = tmp[cursor2]; + return; + } + + var _minGallop = minGallop; + + while (true) { + var count1 = 0; + var count2 = 0; + var exit = false; + + do { + if (compare(tmp[cursor2], array[cursor1]) < 0) { + array[dest--] = array[cursor1--]; + count1++; + count2 = 0; + if (--length1 === 0) { + exit = true; + break; + } + } + else { + array[dest--] = tmp[cursor2--]; + count2++; + count1 = 0; + if (--length2 === 1) { + exit = true; + break; + } + } + } while ((count1 | count2) < _minGallop); + + if (exit) { + break; + } + + do { + count1 = length1 - gallopRight(tmp[cursor2], array, start1, length1, length1 - 1, compare); + + if (count1 !== 0) { + dest -= count1; + cursor1 -= count1; + length1 -= count1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = count1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + if (length1 === 0) { + exit = true; + break; + } + } + + array[dest--] = tmp[cursor2--]; + + if (--length2 === 1) { + exit = true; + break; + } + + count2 = length2 - gallopLeft(array[cursor1], tmp, 0, length2, length2 - 1, compare); + + if (count2 !== 0) { + dest -= count2; + cursor2 -= count2; + length2 -= count2; + customDest = dest + 1; + customCursor = cursor2 + 1; + + for (i = 0; i < count2; i++) { + array[customDest + i] = tmp[customCursor + i]; + } + + if (length2 <= 1) { + exit = true; + break; + } + } + + array[dest--] = array[cursor1--]; + + if (--length1 === 0) { + exit = true; + break; + } + + _minGallop--; + } while (count1 >= DEFAULT_MIN_GALLOPING || count2 >= DEFAULT_MIN_GALLOPING); + + if (exit) { + break; + } + + if (_minGallop < 0) { + _minGallop = 0; + } + + _minGallop += 2; + } + + minGallop = _minGallop; + + if (minGallop < 1) { + minGallop = 1; + } + + if (length2 === 1) { + dest -= length1; + cursor1 -= length1; + customDest = dest + 1; + customCursor = cursor1 + 1; + + for (i = length1 - 1; i >= 0; i--) { + array[customDest + i] = array[customCursor + i]; + } + + array[dest] = tmp[cursor2]; + } + else if (length2 === 0) { + throw new Error(); + // throw new Error('mergeHigh preconditions were not respected'); + } + else { + customCursor = dest - (length2 - 1); + for (i = 0; i < length2; i++) { + array[customCursor + i] = tmp[i]; + } + } + } + + this.mergeRuns = mergeRuns; + this.forceMergeRuns = forceMergeRuns; + this.pushRun = pushRun; + } + + function sort(array, compare, lo, hi) { + if (!lo) { + lo = 0; + } + if (!hi) { + hi = array.length; + } + + var remaining = hi - lo; + + if (remaining < 2) { + return; + } + + var runLength = 0; + + if (remaining < DEFAULT_MIN_MERGE) { + runLength = makeAscendingRun(array, lo, hi, compare); + binaryInsertionSort(array, lo, hi, lo + runLength, compare); + return; + } + + var ts = new TimSort(array, compare); + + var minRun = minRunLength(remaining); + + do { + runLength = makeAscendingRun(array, lo, hi, compare); + if (runLength < minRun) { + var force = remaining; + if (force > minRun) { + force = minRun; + } + + binaryInsertionSort(array, lo, lo + force, lo + runLength, compare); + runLength = force; + } + + ts.pushRun(lo, runLength); + ts.mergeRuns(); + + remaining -= runLength; + lo += runLength; + } while (remaining !== 0); + + ts.forceMergeRuns(); + } + + module.exports = sort; + + +/***/ }, +/* 86 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 动画主类, 调度和管理所有动画控制器 + * + * @module zrender/animation/Animation + * @author pissang(https://github.com/pissang) + */ + // TODO Additive animation + // http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/ + // https://developer.apple.com/videos/wwdc2014/#236 + + + var util = __webpack_require__(4); + var Dispatcher = __webpack_require__(87).Dispatcher; + + var requestAnimationFrame = __webpack_require__(88); + + var Animator = __webpack_require__(36); + /** + * @typedef {Object} IZRenderStage + * @property {Function} update + */ + + /** + * @alias module:zrender/animation/Animation + * @constructor + * @param {Object} [options] + * @param {Function} [options.onframe] + * @param {IZRenderStage} [options.stage] + * @example + * var animation = new Animation(); + * var obj = { + * x: 100, + * y: 100 + * }; + * animation.animate(node.position) + * .when(1000, { + * x: 500, + * y: 500 + * }) + * .when(2000, { + * x: 100, + * y: 100 + * }) + * .start('spline'); + */ + var Animation = function (options) { + + options = options || {}; + + this.stage = options.stage || {}; + + this.onframe = options.onframe || function() {}; + + // private properties + this._clips = []; + + this._running = false; + + this._time; + + this._pausedTime; + + this._pauseStart; + + this._paused = false; + + Dispatcher.call(this); + }; + + Animation.prototype = { + + constructor: Animation, + /** + * 添加 clip + * @param {module:zrender/animation/Clip} clip + */ + addClip: function (clip) { + this._clips.push(clip); + }, + /** + * 添加 animator + * @param {module:zrender/animation/Animator} animator + */ + addAnimator: function (animator) { + animator.animation = this; + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.addClip(clips[i]); + } + }, + /** + * 删除动画片段 + * @param {module:zrender/animation/Clip} clip + */ + removeClip: function(clip) { + var idx = util.indexOf(this._clips, clip); + if (idx >= 0) { + this._clips.splice(idx, 1); + } + }, + + /** + * 删除动画片段 + * @param {module:zrender/animation/Animator} animator + */ + removeAnimator: function (animator) { + var clips = animator.getClips(); + for (var i = 0; i < clips.length; i++) { + this.removeClip(clips[i]); + } + animator.animation = null; + }, + + _update: function() { + + var time = new Date().getTime() - this._pausedTime; + var delta = time - this._time; + var clips = this._clips; + var len = clips.length; + + var deferredEvents = []; + var deferredClips = []; + for (var i = 0; i < len; i++) { + var clip = clips[i]; + var e = clip.step(time); + // Throw out the events need to be called after + // stage.update, like destroy + if (e) { + deferredEvents.push(e); + deferredClips.push(clip); + } + } + + // Remove the finished clip + for (var i = 0; i < len;) { + if (clips[i]._needsRemove) { + clips[i] = clips[len - 1]; + clips.pop(); + len--; + } + else { + i++; + } + } + + len = deferredEvents.length; + for (var i = 0; i < len; i++) { + deferredClips[i].fire(deferredEvents[i]); + } + + this._time = time; + + this.onframe(delta); + + this.trigger('frame', delta); + + if (this.stage.update) { + this.stage.update(); + } + }, + + _startLoop: function () { + var self = this; + + this._running = true; + + function step() { + if (self._running) { + + requestAnimationFrame(step); + + !self._paused && self._update(); + } + } + + requestAnimationFrame(step); + }, + + /** + * 开始运行动画 + */ + start: function () { + + this._time = new Date().getTime(); + this._pausedTime = 0; + + this._startLoop(); + }, + /** + * 停止运行动画 + */ + stop: function () { + this._running = false; + }, + + /** + * Pause + */ + pause: function () { + if (!this._paused) { + this._pauseStart = new Date().getTime(); + this._paused = true; + } + }, + + /** + * Resume + */ + resume: function () { + if (this._paused) { + this._pausedTime += (new Date().getTime()) - this._pauseStart; + this._paused = false; + } + }, + + /** + * 清除所有动画片段 + */ + clear: function () { + this._clips = []; + }, + /** + * 对一个目标创建一个animator对象,可以指定目标中的属性使用动画 + * @param {Object} target + * @param {Object} options + * @param {boolean} [options.loop=false] 是否循环播放动画 + * @param {Function} [options.getter=null] + * 如果指定getter函数,会通过getter函数取属性值 + * @param {Function} [options.setter=null] + * 如果指定setter函数,会通过setter函数设置属性值 + * @return {module:zrender/animation/Animation~Animator} + */ + // TODO Gap + animate: function (target, options) { + options = options || {}; + var animator = new Animator( + target, + options.loop, + options.getter, + options.setter + ); + + return animator; + } + }; + + util.mixin(Animation, Dispatcher); + + module.exports = Animation; + + + +/***/ }, +/* 87 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * 事件辅助类 + * @module zrender/core/event + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + */ + + + var Eventful = __webpack_require__(33); + + var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener; + + function getBoundingClientRect(el) { + // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect + return el.getBoundingClientRect ? el.getBoundingClientRect() : {left: 0, top: 0}; + } + + function clientToLocal(el, e, out) { + // clientX/clientY is according to view port. + var box = getBoundingClientRect(el); + out = out || {}; + out.zrX = e.clientX - box.left; + out.zrY = e.clientY - box.top; + return out; + } + + /** + * 如果存在第三方嵌入的一些dom触发的事件,或touch事件,需要转换一下事件坐标 + */ + function normalizeEvent(el, e) { + + e = e || window.event; + + if (e.zrX != null) { + return e; + } + + var eventType = e.type; + var isTouch = eventType && eventType.indexOf('touch') >= 0; + + if (!isTouch) { + clientToLocal(el, e, e); + e.zrDelta = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3; + } + else { + var touch = eventType != 'touchend' + ? e.targetTouches[0] + : e.changedTouches[0]; + touch && clientToLocal(el, touch, e); + } + + return e; + } + + function addEventListener(el, name, handler) { + if (isDomLevel2) { + el.addEventListener(name, handler); + } + else { + el.attachEvent('on' + name, handler); + } + } + + function removeEventListener(el, name, handler) { + if (isDomLevel2) { + el.removeEventListener(name, handler); + } + else { + el.detachEvent('on' + name, handler); + } + } + + /** + * 停止冒泡和阻止默认行为 + * @memberOf module:zrender/core/event + * @method + * @param {Event} e : event对象 + */ + var stop = isDomLevel2 + ? function (e) { + e.preventDefault(); + e.stopPropagation(); + e.cancelBubble = true; + } + : function (e) { + e.returnValue = false; + e.cancelBubble = true; + }; + + module.exports = { + clientToLocal: clientToLocal, + normalizeEvent: normalizeEvent, + addEventListener: addEventListener, + removeEventListener: removeEventListener, + + stop: stop, + // 做向上兼容 + Dispatcher: Eventful + }; + + + +/***/ }, +/* 88 */ +/***/ function(module, exports) { + + + + module.exports = (typeof window !== 'undefined' && + (window.requestAnimationFrame + || window.msRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.webkitRequestAnimationFrame)) + || function (func) { + setTimeout(func, 16); + }; + + + +/***/ }, +/* 89 */ +/***/ function(module, exports, __webpack_require__) { + + + + var eventTool = __webpack_require__(87); + var zrUtil = __webpack_require__(4); + var Eventful = __webpack_require__(33); + var env = __webpack_require__(2); + var GestureMgr = __webpack_require__(90); + + var addEventListener = eventTool.addEventListener; + var removeEventListener = eventTool.removeEventListener; + var normalizeEvent = eventTool.normalizeEvent; + + var TOUCH_CLICK_DELAY = 300; + + var mouseHandlerNames = [ + 'click', 'dblclick', 'mousewheel', 'mouseout', + 'mouseup', 'mousedown', 'mousemove' + ]; + + var touchHandlerNames = [ + 'touchstart', 'touchend', 'touchmove' + ]; + + function eventNameFix(name) { + return (name === 'mousewheel' && env.browser.firefox) ? 'DOMMouseScroll' : name; + } + + function processGesture(proxy, event, stage) { + var gestureMgr = proxy._gestureMgr; + + stage === 'start' && gestureMgr.clear(); + + var gestureInfo = gestureMgr.recognize( + event, + proxy.handler.findHover(event.zrX, event.zrY, null), + proxy.dom + ); + + stage === 'end' && gestureMgr.clear(); + + if (gestureInfo) { + // eventTool.stop(event); + var type = gestureInfo.type; + event.gestureEvent = type; + + proxy.handler.dispatchToElement(gestureInfo.target, type, gestureInfo.event); + } + } + + /** + * Prevent mouse event from being dispatched after Touch Events action + * @see + * 1. Mobile browsers dispatch mouse events 300ms after touchend. + * 2. Chrome for Android dispatch mousedown for long-touch about 650ms + * Result: Blocking Mouse Events for 700ms. + */ + function setTouchTimer(instance) { + instance._touching = true; + clearTimeout(instance._touchTimer); + instance._touchTimer = setTimeout(function () { + instance._touching = false; + }, 700); + } + + function useTouchEvent() { + return env.touchEventsSupported; + } + + var domHandlers = { + /** + * Mouse move handler + * @inner + * @param {Event} event + */ + mousemove: function (event) { + event = normalizeEvent(this.dom, event); + + this.trigger('mousemove', event); + }, + + /** + * Mouse out handler + * @inner + * @param {Event} event + */ + mouseout: function (event) { + event = normalizeEvent(this.dom, event); + + var element = event.toElement || event.relatedTarget; + if (element != this.dom) { + while (element && element.nodeType != 9) { + // 忽略包含在root中的dom引起的mouseOut + if (element === this.dom) { + return; + } + + element = element.parentNode; + } + } + + this.trigger('mouseout', event); + }, + + /** + * Touch开始响应函数 + * @inner + * @param {Event} event + */ + touchstart: function (event) { + // Default mouse behaviour should not be disabled here. + // For example, page may needs to be slided. + + event = normalizeEvent(this.dom, event); + + this._lastTouchMoment = new Date(); + + processGesture(this, event, 'start'); + + // 平板补充一次findHover + // this._mobileFindFixed(event); + // Trigger mousemove and mousedown + domHandlers.mousemove.call(this, event); + + domHandlers.mousedown.call(this, event); + + setTouchTimer(this); + }, + + /** + * Touch移动响应函数 + * @inner + * @param {Event} event + */ + touchmove: function (event) { + + event = normalizeEvent(this.dom, event); + + processGesture(this, event, 'change'); + + // Mouse move should always be triggered no matter whether + // there is gestrue event, because mouse move and pinch may + // be used at the same time. + domHandlers.mousemove.call(this, event); + + setTouchTimer(this); + }, + + /** + * Touch结束响应函数 + * @inner + * @param {Event} event + */ + touchend: function (event) { + + event = normalizeEvent(this.dom, event); + + processGesture(this, event, 'end'); + + domHandlers.mouseup.call(this, event); + + // click event should always be triggered no matter whether + // there is gestrue event. System click can not be prevented. + if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) { + domHandlers.click.call(this, event); + } + + setTouchTimer(this); + } + }; + + // Common handlers + zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick'], function (name) { + domHandlers[name] = function (event) { + event = normalizeEvent(this.dom, event); + this.trigger(name, event); + }; + }); + + /** + * 为控制类实例初始化dom 事件处理函数 + * + * @inner + * @param {module:zrender/Handler} instance 控制类实例 + */ + function initDomHandler(instance) { + for (var i = 0; i < touchHandlerNames.length; i++) { + var name = touchHandlerNames[i]; + instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); + } + + for (var i = 0; i < mouseHandlerNames.length; i++) { + var name = mouseHandlerNames[i]; + instance._handlers[name] = makeMouseHandler(domHandlers[name], instance); + } + + function makeMouseHandler(fn, instance) { + return function () { + if (instance._touching) { + return; + } + return fn.apply(instance, arguments); + }; + } + } + + + function HandlerDomProxy(dom) { + Eventful.call(this); + + this.dom = dom; + + /** + * @private + * @type {boolean} + */ + this._touching = false; + + /** + * @private + * @type {number} + */ + this._touchTimer; + + /** + * @private + * @type {module:zrender/core/GestureMgr} + */ + this._gestureMgr = new GestureMgr(); + + this._handlers = {}; + + initDomHandler(this); + + if (useTouchEvent()) { + mountHandlers(touchHandlerNames, this); + + // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. + // addEventListener(root, 'mouseout', this._mouseoutHandler); + } + + // Considering some devices that both enable touch and mouse event (like MS Surface + // and lenovo X240, @see #2350), we make mouse event be always listened, otherwise + // mouse event can not be handle in those devices. + mountHandlers(mouseHandlerNames, this); + + function mountHandlers(handlerNames, instance) { + zrUtil.each(handlerNames, function (name) { + addEventListener(dom, eventNameFix(name), instance._handlers[name]); + }, instance); + } + } + + var handlerDomProxyProto = HandlerDomProxy.prototype; + handlerDomProxyProto.dispose = function () { + var handlerNames = mouseHandlerNames.concat(touchHandlerNames); + + for (var i = 0; i < handlerNames.length; i++) { + var name = handlerNames[i]; + removeEventListener(this.dom, eventNameFix(name), this._handlers[name]); + } + }; + + handlerDomProxyProto.setCursor = function (cursorStyle) { + this.dom.style.cursor = cursorStyle || 'default'; + }; + + zrUtil.mixin(HandlerDomProxy, Eventful); + + module.exports = HandlerDomProxy; + + +/***/ }, +/* 90 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Only implements needed gestures for mobile. + */ + + + var eventUtil = __webpack_require__(87); + + var GestureMgr = function () { + + /** + * @private + * @type {Array.} + */ + this._track = []; + }; + + GestureMgr.prototype = { + + constructor: GestureMgr, + + recognize: function (event, target, root) { + this._doTrack(event, target, root); + return this._recognize(event); + }, + + clear: function () { + this._track.length = 0; + return this; + }, + + _doTrack: function (event, target, root) { + var touches = event.touches; + + if (!touches) { + return; + } + + var trackItem = { + points: [], + touches: [], + target: target, + event: event + }; + + for (var i = 0, len = touches.length; i < len; i++) { + var touch = touches[i]; + var pos = eventUtil.clientToLocal(root, touch); + trackItem.points.push([pos.zrX, pos.zrY]); + trackItem.touches.push(touch); + } + + this._track.push(trackItem); + }, + + _recognize: function (event) { + for (var eventName in recognizers) { + if (recognizers.hasOwnProperty(eventName)) { + var gestureInfo = recognizers[eventName](this._track, event); + if (gestureInfo) { + return gestureInfo; + } + } + } + } + }; + + function dist(pointPair) { + var dx = pointPair[1][0] - pointPair[0][0]; + var dy = pointPair[1][1] - pointPair[0][1]; + + return Math.sqrt(dx * dx + dy * dy); + } + + function center(pointPair) { + return [ + (pointPair[0][0] + pointPair[1][0]) / 2, + (pointPair[0][1] + pointPair[1][1]) / 2 + ]; + } + + var recognizers = { + + pinch: function (track, event) { + var trackLen = track.length; + + if (!trackLen) { + return; + } + + var pinchEnd = (track[trackLen - 1] || {}).points; + var pinchPre = (track[trackLen - 2] || {}).points || pinchEnd; + + if (pinchPre + && pinchPre.length > 1 + && pinchEnd + && pinchEnd.length > 1 + ) { + var pinchScale = dist(pinchEnd) / dist(pinchPre); + !isFinite(pinchScale) && (pinchScale = 1); + + event.pinchScale = pinchScale; + + var pinchCenter = center(pinchEnd); + event.pinchX = pinchCenter[0]; + event.pinchY = pinchCenter[1]; + + return { + type: 'pinch', + target: track[0].target, + event: event + }; + } + } + + // Only pinch currently. + }; + + module.exports = GestureMgr; + + + +/***/ }, +/* 91 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Default canvas painter + * @module zrender/Painter + * @author Kener (@Kener-林峰, kener.linfeng@gmail.com) + * errorrik (errorrik@gmail.com) + * pissang (https://www.github.com/pissang) + */ + + + var config = __webpack_require__(41); + var util = __webpack_require__(4); + var log = __webpack_require__(40); + var BoundingRect = __webpack_require__(9); + var timsort = __webpack_require__(85); + + var Layer = __webpack_require__(92); + + var requestAnimationFrame = __webpack_require__(88); + + // PENDIGN + // Layer exceeds MAX_PROGRESSIVE_LAYER_NUMBER may have some problem when flush directly second time. + // + // Maximum progressive layer. When exceeding this number. All elements will be drawed in the last layer. + var MAX_PROGRESSIVE_LAYER_NUMBER = 5; + + function parseInt10(val) { + return parseInt(val, 10); + } + + function isLayerValid(layer) { + if (!layer) { + return false; + } + + if (layer.isBuildin) { + return true; + } + + if (typeof(layer.resize) !== 'function' + || typeof(layer.refresh) !== 'function' + ) { + return false; + } + + return true; + } + + function preProcessLayer(layer) { + layer.__unusedCount++; + } + + function postProcessLayer(layer) { + if (layer.__unusedCount == 1) { + layer.clear(); + } + } + + var tmpRect = new BoundingRect(0, 0, 0, 0); + var viewRect = new BoundingRect(0, 0, 0, 0); + function isDisplayableCulled(el, width, height) { + tmpRect.copy(el.getBoundingRect()); + if (el.transform) { + tmpRect.applyTransform(el.transform); + } + viewRect.width = width; + viewRect.height = height; + return !tmpRect.intersect(viewRect); + } + + function isClipPathChanged(clipPaths, prevClipPaths) { + if (clipPaths == prevClipPaths) { // Can both be null or undefined + return false; + } + + if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) { + return true; + } + for (var i = 0; i < clipPaths.length; i++) { + if (clipPaths[i] !== prevClipPaths[i]) { + return true; + } + } + } + + function doClip(clipPaths, ctx) { + for (var i = 0; i < clipPaths.length; i++) { + var clipPath = clipPaths[i]; + var path = clipPath.path; + + clipPath.setTransform(ctx); + path.beginPath(ctx); + clipPath.buildPath(path, clipPath.shape); + ctx.clip(); + // Transform back + clipPath.restoreTransform(ctx); + } + } + + function createRoot(width, height) { + var domRoot = document.createElement('div'); + var domRootStyle = domRoot.style; + + // domRoot.onselectstart = returnFalse; // 避免页面选中的尴尬 + domRootStyle.position = 'relative'; + domRootStyle.overflow = 'hidden'; + domRootStyle.width = width + 'px'; + domRootStyle.height = height + 'px'; + return domRoot; + } + + /** + * @alias module:zrender/Painter + * @constructor + * @param {HTMLElement} root 绘图容器 + * @param {module:zrender/Storage} storage + * @param {Ojbect} opts + */ + var Painter = function (root, storage, opts) { + // In node environment using node-canvas + var singleCanvas = !root.nodeName // In node ? + || root.nodeName.toUpperCase() === 'CANVAS'; + + opts = opts || {}; + + /** + * @type {number} + */ + this.dpr = opts.devicePixelRatio || config.devicePixelRatio; + /** + * @type {boolean} + * @private + */ + this._singleCanvas = singleCanvas; + /** + * 绘图容器 + * @type {HTMLElement} + */ + this.root = root; + + var rootStyle = root.style; + + if (rootStyle) { + rootStyle['-webkit-tap-highlight-color'] = 'transparent'; + rootStyle['-webkit-user-select'] = + rootStyle['user-select'] = + rootStyle['-webkit-touch-callout'] = 'none'; + + root.innerHTML = ''; + } + + /** + * @type {module:zrender/Storage} + */ + this.storage = storage; + + /** + * @type {Array.} + * @private + */ + var zlevelList = this._zlevelList = []; + + /** + * @type {Object.} + * @private + */ + var layers = this._layers = {}; + + /** + * @type {Object.} + * @type {private} + */ + this._layerConfig = {}; + + if (!singleCanvas) { + this._width = this._getWidth(); + this._height = this._getHeight(); + + var domRoot = this._domRoot = createRoot( + this._width, this._height + ); + root.appendChild(domRoot); + } + else { + // Use canvas width and height directly + var width = root.width; + var height = root.height; + this._width = width; + this._height = height; + + // Create layer if only one given canvas + // Device pixel ratio is fixed to 1 because given canvas has its specified width and height + var mainLayer = new Layer(root, this, 1); + mainLayer.initContext(); + // FIXME Use canvas width and height + // mainLayer.resize(width, height); + layers[0] = mainLayer; + zlevelList.push(0); + } + + this.pathToImage = this._createPathToImage(); + + // Layers for progressive rendering + this._progressiveLayers = []; + + /** + * @type {module:zrender/Layer} + * @private + */ + this._hoverlayer; + + this._hoverElements = []; + }; + + Painter.prototype = { + + constructor: Painter, + + /** + * If painter use a single canvas + * @return {boolean} + */ + isSingleCanvas: function () { + return this._singleCanvas; + }, + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._singleCanvas ? this._layers[0].dom : this._domRoot; + }, + + /** + * 刷新 + * @param {boolean} [paintAll=false] 强制绘制所有displayable + */ + refresh: function (paintAll) { + + var list = this.storage.getDisplayList(true); + + var zlevelList = this._zlevelList; + + this._paintList(list, paintAll); + + // Paint custum layers + for (var i = 0; i < zlevelList.length; i++) { + var z = zlevelList[i]; + var layer = this._layers[z]; + if (!layer.isBuildin && layer.refresh) { + layer.refresh(); + } + } + + this.refreshHover(); + + if (this._progressiveLayers.length) { + this._startProgessive(); + } + + return this; + }, + + addHover: function (el, hoverStyle) { + if (el.__hoverMir) { + return; + } + var elMirror = new el.constructor({ + style: el.style, + shape: el.shape + }); + elMirror.__from = el; + el.__hoverMir = elMirror; + elMirror.setStyle(hoverStyle); + this._hoverElements.push(elMirror); + }, + + removeHover: function (el) { + var elMirror = el.__hoverMir; + var hoverElements = this._hoverElements; + var idx = util.indexOf(hoverElements, elMirror); + if (idx >= 0) { + hoverElements.splice(idx, 1); + } + el.__hoverMir = null; + }, + + clearHover: function (el) { + var hoverElements = this._hoverElements; + for (var i = 0; i < hoverElements.length; i++) { + var from = hoverElements[i].__from; + if (from) { + from.__hoverMir = null; + } + } + hoverElements.length = 0; + }, + + refreshHover: function () { + var hoverElements = this._hoverElements; + var len = hoverElements.length; + var hoverLayer = this._hoverlayer; + hoverLayer && hoverLayer.clear(); + + if (!len) { + return; + } + timsort(hoverElements, this.storage.displayableSortFunc); + + // Use a extream large zlevel + // FIXME? + if (!hoverLayer) { + hoverLayer = this._hoverlayer = this.getLayer(1e5); + } + + var scope = {}; + hoverLayer.ctx.save(); + for (var i = 0; i < len;) { + var el = hoverElements[i]; + var originalEl = el.__from; + // Original el is removed + // PENDING + if (!(originalEl && originalEl.__zr)) { + hoverElements.splice(i, 1); + originalEl.__hoverMir = null; + len--; + continue; + } + i++; + + // Use transform + // FIXME style and shape ? + if (!originalEl.invisible) { + el.transform = originalEl.transform; + el.invTransform = originalEl.invTransform; + el.__clipPaths = originalEl.__clipPaths; + // el. + this._doPaintEl(el, hoverLayer, true, scope); + } + } + hoverLayer.ctx.restore(); + }, + + _startProgessive: function () { + var self = this; + + if (!self._furtherProgressive) { + return; + } + + // Use a token to stop progress steps triggered by + // previous zr.refresh calling. + var token = self._progressiveToken = +new Date(); + + self._progress++; + requestAnimationFrame(step); + + function step() { + // In case refreshed or disposed + if (token === self._progressiveToken && self.storage) { + + self._doPaintList(self.storage.getDisplayList()); + + if (self._furtherProgressive) { + self._progress++; + requestAnimationFrame(step); + } + else { + self._progressiveToken = -1; + } + } + } + }, + + _clearProgressive: function () { + this._progressiveToken = -1; + this._progress = 0; + util.each(this._progressiveLayers, function (layer) { + layer.__dirty && layer.clear(); + }); + }, + + _paintList: function (list, paintAll) { + + if (paintAll == null) { + paintAll = false; + } + + this._updateLayerStatus(list); + + this._clearProgressive(); + + this.eachBuildinLayer(preProcessLayer); + + this._doPaintList(list, paintAll); + + this.eachBuildinLayer(postProcessLayer); + }, + + _doPaintList: function (list, paintAll) { + var currentLayer; + var currentZLevel; + var ctx; + + // var invTransform = []; + var scope; + + var progressiveLayerIdx = 0; + var currentProgressiveLayer; + + var width = this._width; + var height = this._height; + var layerProgress; + var frame = this._progress; + function flushProgressiveLayer(layer) { + var dpr = ctx.dpr || 1; + ctx.save(); + ctx.globalAlpha = 1; + ctx.shadowBlur = 0; + // Avoid layer don't clear in next progressive frame + currentLayer.__dirty = true; + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.drawImage(layer.dom, 0, 0, width * dpr, height * dpr); + ctx.restore(); + } + + for (var i = 0, l = list.length; i < l; i++) { + var el = list[i]; + var elZLevel = this._singleCanvas ? 0 : el.zlevel; + + var elFrame = el.__frame; + + // Flush at current context + // PENDING + if (elFrame < 0 && currentProgressiveLayer) { + flushProgressiveLayer(currentProgressiveLayer); + currentProgressiveLayer = null; + } + + // Change draw layer + if (currentZLevel !== elZLevel) { + if (ctx) { + ctx.restore(); + } + + // Reset scope + scope = {}; + + // Only 0 zlevel if only has one canvas + currentZLevel = elZLevel; + currentLayer = this.getLayer(currentZLevel); + + if (!currentLayer.isBuildin) { + log( + 'ZLevel ' + currentZLevel + + ' has been used by unkown layer ' + currentLayer.id + ); + } + + ctx = currentLayer.ctx; + ctx.save(); + + // Reset the count + currentLayer.__unusedCount = 0; + + if (currentLayer.__dirty || paintAll) { + currentLayer.clear(); + } + } + + if (!(currentLayer.__dirty || paintAll)) { + continue; + } + + if (elFrame >= 0) { + // Progressive layer changed + if (!currentProgressiveLayer) { + currentProgressiveLayer = this._progressiveLayers[ + Math.min(progressiveLayerIdx++, MAX_PROGRESSIVE_LAYER_NUMBER - 1) + ]; + + currentProgressiveLayer.ctx.save(); + currentProgressiveLayer.renderScope = {}; + + if (currentProgressiveLayer + && (currentProgressiveLayer.__progress > currentProgressiveLayer.__maxProgress) + ) { + // flushProgressiveLayer(currentProgressiveLayer); + // Quick jump all progressive elements + // All progressive element are not dirty, jump over and flush directly + i = currentProgressiveLayer.__nextIdxNotProg - 1; + // currentProgressiveLayer = null; + continue; + } + + layerProgress = currentProgressiveLayer.__progress; + + if (!currentProgressiveLayer.__dirty) { + // Keep rendering + frame = layerProgress; + } + + currentProgressiveLayer.__progress = frame + 1; + } + + if (elFrame === frame) { + this._doPaintEl(el, currentProgressiveLayer, true, currentProgressiveLayer.renderScope); + } + } + else { + this._doPaintEl(el, currentLayer, paintAll, scope); + } + + el.__dirty = false; + } + + if (currentProgressiveLayer) { + flushProgressiveLayer(currentProgressiveLayer); + } + + // Restore the lastLayer ctx + ctx && ctx.restore(); + // If still has clipping state + // if (scope.prevElClipPaths) { + // ctx.restore(); + // } + + this._furtherProgressive = false; + util.each(this._progressiveLayers, function (layer) { + if (layer.__maxProgress >= layer.__progress) { + this._furtherProgressive = true; + } + }, this); + }, + + _doPaintEl: function (el, currentLayer, forcePaint, scope) { + var ctx = currentLayer.ctx; + var m = el.transform; + if ( + (currentLayer.__dirty || forcePaint) + // Ignore invisible element + && !el.invisible + // Ignore transparent element + && el.style.opacity !== 0 + // Ignore scale 0 element, in some environment like node-canvas + // Draw a scale 0 element can cause all following draw wrong + // And setTransform with scale 0 will cause set back transform failed. + && !(m && !m[0] && !m[3]) + // Ignore culled element + && !(el.culling && isDisplayableCulled(el, this._width, this._height)) + ) { + + var clipPaths = el.__clipPaths; + + // Optimize when clipping on group with several elements + if (scope.prevClipLayer !== currentLayer + || isClipPathChanged(clipPaths, scope.prevElClipPaths) + ) { + // If has previous clipping state, restore from it + if (scope.prevElClipPaths) { + scope.prevClipLayer.ctx.restore(); + scope.prevClipLayer = scope.prevElClipPaths = null; + + // Reset prevEl since context has been restored + scope.prevEl = null; + } + // New clipping state + if (clipPaths) { + ctx.save(); + doClip(clipPaths, ctx); + scope.prevClipLayer = currentLayer; + scope.prevElClipPaths = clipPaths; + } + } + el.beforeBrush && el.beforeBrush(ctx); + + el.brush(ctx, scope.prevEl || null); + scope.prevEl = el; + + el.afterBrush && el.afterBrush(ctx); + } + }, + + /** + * 获取 zlevel 所在层,如果不存在则会创建一个新的层 + * @param {number} zlevel + * @return {module:zrender/Layer} + */ + getLayer: function (zlevel) { + if (this._singleCanvas) { + return this._layers[0]; + } + + var layer = this._layers[zlevel]; + if (!layer) { + // Create a new layer + layer = new Layer('zr_' + zlevel, this, this.dpr); + layer.isBuildin = true; + + if (this._layerConfig[zlevel]) { + util.merge(layer, this._layerConfig[zlevel], true); + } + + this.insertLayer(zlevel, layer); + + // Context is created after dom inserted to document + // Or excanvas will get 0px clientWidth and clientHeight + layer.initContext(); + } + + return layer; + }, + + insertLayer: function (zlevel, layer) { + + var layersMap = this._layers; + var zlevelList = this._zlevelList; + var len = zlevelList.length; + var prevLayer = null; + var i = -1; + var domRoot = this._domRoot; + + if (layersMap[zlevel]) { + log('ZLevel ' + zlevel + ' has been used already'); + return; + } + // Check if is a valid layer + if (!isLayerValid(layer)) { + log('Layer of zlevel ' + zlevel + ' is not valid'); + return; + } + + if (len > 0 && zlevel > zlevelList[0]) { + for (i = 0; i < len - 1; i++) { + if ( + zlevelList[i] < zlevel + && zlevelList[i + 1] > zlevel + ) { + break; + } + } + prevLayer = layersMap[zlevelList[i]]; + } + zlevelList.splice(i + 1, 0, zlevel); + + if (prevLayer) { + var prevDom = prevLayer.dom; + if (prevDom.nextSibling) { + domRoot.insertBefore( + layer.dom, + prevDom.nextSibling + ); + } + else { + domRoot.appendChild(layer.dom); + } + } + else { + if (domRoot.firstChild) { + domRoot.insertBefore(layer.dom, domRoot.firstChild); + } + else { + domRoot.appendChild(layer.dom); + } + } + + layersMap[zlevel] = layer; + }, + + // Iterate each layer + eachLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + cb.call(context, this._layers[z], z); + } + }, + + // Iterate each buildin layer + eachBuildinLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var layer; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + layer = this._layers[z]; + if (layer.isBuildin) { + cb.call(context, layer, z); + } + } + }, + + // Iterate each other layer except buildin layer + eachOtherLayer: function (cb, context) { + var zlevelList = this._zlevelList; + var layer; + var z; + var i; + for (i = 0; i < zlevelList.length; i++) { + z = zlevelList[i]; + layer = this._layers[z]; + if (! layer.isBuildin) { + cb.call(context, layer, z); + } + } + }, + + /** + * 获取所有已创建的层 + * @param {Array.} [prevLayer] + */ + getLayers: function () { + return this._layers; + }, + + _updateLayerStatus: function (list) { + + var layers = this._layers; + var progressiveLayers = this._progressiveLayers; + + var elCountsLastFrame = {}; + var progressiveElCountsLastFrame = {}; + + this.eachBuildinLayer(function (layer, z) { + elCountsLastFrame[z] = layer.elCount; + layer.elCount = 0; + layer.__dirty = false; + }); + + util.each(progressiveLayers, function (layer, idx) { + progressiveElCountsLastFrame[idx] = layer.elCount; + layer.elCount = 0; + layer.__dirty = false; + }); + + var progressiveLayerCount = 0; + var currentProgressiveLayer; + var lastProgressiveKey; + var frameCount = 0; + for (var i = 0, l = list.length; i < l; i++) { + var el = list[i]; + var zlevel = this._singleCanvas ? 0 : el.zlevel; + var layer = layers[zlevel]; + var elProgress = el.progressive; + if (layer) { + layer.elCount++; + layer.__dirty = layer.__dirty || el.__dirty; + } + + /////// Update progressive + if (elProgress >= 0) { + // Fix wrong progressive sequence problem. + if (lastProgressiveKey !== elProgress) { + lastProgressiveKey = elProgress; + frameCount++; + } + var elFrame = el.__frame = frameCount - 1; + if (!currentProgressiveLayer) { + var idx = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER - 1); + currentProgressiveLayer = progressiveLayers[idx]; + if (!currentProgressiveLayer) { + currentProgressiveLayer = progressiveLayers[idx] = new Layer( + 'progressive', this, this.dpr + ); + currentProgressiveLayer.initContext(); + } + currentProgressiveLayer.__maxProgress = 0; + } + currentProgressiveLayer.__dirty = currentProgressiveLayer.__dirty || el.__dirty; + currentProgressiveLayer.elCount++; + + currentProgressiveLayer.__maxProgress = Math.max( + currentProgressiveLayer.__maxProgress, elFrame + ); + + if (currentProgressiveLayer.__maxProgress >= currentProgressiveLayer.__progress) { + // Should keep rendering this layer because progressive rendering is not finished yet + layer.__dirty = true; + } + } + else { + el.__frame = -1; + + if (currentProgressiveLayer) { + currentProgressiveLayer.__nextIdxNotProg = i; + progressiveLayerCount++; + currentProgressiveLayer = null; + } + } + } + + if (currentProgressiveLayer) { + progressiveLayerCount++; + currentProgressiveLayer.__nextIdxNotProg = i; + } + + // 层中的元素数量有发生变化 + this.eachBuildinLayer(function (layer, z) { + if (elCountsLastFrame[z] !== layer.elCount) { + layer.__dirty = true; + } + }); + + progressiveLayers.length = Math.min(progressiveLayerCount, MAX_PROGRESSIVE_LAYER_NUMBER); + util.each(progressiveLayers, function (layer, idx) { + if (progressiveElCountsLastFrame[idx] !== layer.elCount) { + el.__dirty = true; + } + if (layer.__dirty) { + layer.__progress = 0; + } + }); + }, + + /** + * 清除hover层外所有内容 + */ + clear: function () { + this.eachBuildinLayer(this._clearLayer); + return this; + }, + + _clearLayer: function (layer) { + layer.clear(); + }, + + /** + * 修改指定zlevel的绘制参数 + * + * @param {string} zlevel + * @param {Object} config 配置对象 + * @param {string} [config.clearColor=0] 每次清空画布的颜色 + * @param {string} [config.motionBlur=false] 是否开启动态模糊 + * @param {number} [config.lastFrameAlpha=0.7] + * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显 + */ + configLayer: function (zlevel, config) { + if (config) { + var layerConfig = this._layerConfig; + if (!layerConfig[zlevel]) { + layerConfig[zlevel] = config; + } + else { + util.merge(layerConfig[zlevel], config, true); + } + + var layer = this._layers[zlevel]; + + if (layer) { + util.merge(layer, layerConfig[zlevel], true); + } + } + }, + + /** + * 删除指定层 + * @param {number} zlevel 层所在的zlevel + */ + delLayer: function (zlevel) { + var layers = this._layers; + var zlevelList = this._zlevelList; + var layer = layers[zlevel]; + if (!layer) { + return; + } + layer.dom.parentNode.removeChild(layer.dom); + delete layers[zlevel]; + + zlevelList.splice(util.indexOf(zlevelList, zlevel), 1); + }, + + /** + * 区域大小变化后重绘 + */ + resize: function (width, height) { + var domRoot = this._domRoot; + // FIXME Why ? + domRoot.style.display = 'none'; + + width = width || this._getWidth(); + height = height || this._getHeight(); + + domRoot.style.display = ''; + + // 优化没有实际改变的resize + if (this._width != width || height != this._height) { + domRoot.style.width = width + 'px'; + domRoot.style.height = height + 'px'; + + for (var id in this._layers) { + this._layers[id].resize(width, height); + } + + this.refresh(true); + } + + this._width = width; + this._height = height; + + return this; + }, + + /** + * 清除单独的一个层 + * @param {number} zlevel + */ + clearLayer: function (zlevel) { + var layer = this._layers[zlevel]; + if (layer) { + layer.clear(); + } + }, + + /** + * 释放 + */ + dispose: function () { + this.root.innerHTML = ''; + + this.root = + this.storage = + + this._domRoot = + this._layers = null; + }, + + /** + * Get canvas which has all thing rendered + * @param {Object} opts + * @param {string} [opts.backgroundColor] + */ + getRenderedCanvas: function (opts) { + opts = opts || {}; + if (this._singleCanvas) { + return this._layers[0].dom; + } + + var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); + imageLayer.initContext(); + + imageLayer.clearColor = opts.backgroundColor; + imageLayer.clear(); + + var displayList = this.storage.getDisplayList(true); + + var scope = {}; + for (var i = 0; i < displayList.length; i++) { + var el = displayList[i]; + this._doPaintEl(el, imageLayer, true, scope); + } + + return imageLayer.dom; + }, + /** + * 获取绘图区域宽度 + */ + getWidth: function () { + return this._width; + }, + + /** + * 获取绘图区域高度 + */ + getHeight: function () { + return this._height; + }, + + _getWidth: function () { + var root = this.root; + var stl = document.defaultView.getComputedStyle(root); + + // FIXME Better way to get the width and height when element has not been append to the document + return ((root.clientWidth || parseInt10(stl.width) || parseInt10(root.style.width)) + - (parseInt10(stl.paddingLeft) || 0) + - (parseInt10(stl.paddingRight) || 0)) | 0; + }, + + _getHeight: function () { + var root = this.root; + var stl = document.defaultView.getComputedStyle(root); + + return ((root.clientHeight || parseInt10(stl.height) || parseInt10(root.style.height)) + - (parseInt10(stl.paddingTop) || 0) + - (parseInt10(stl.paddingBottom) || 0)) | 0; + }, + + _pathToImage: function (id, path, width, height, dpr) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + canvas.width = width * dpr; + canvas.height = height * dpr; + + ctx.clearRect(0, 0, width * dpr, height * dpr); + + var pathTransform = { + position: path.position, + rotation: path.rotation, + scale: path.scale + }; + path.position = [0, 0, 0]; + path.rotation = 0; + path.scale = [1, 1]; + if (path) { + path.brush(ctx); + } + + var ImageShape = __webpack_require__(62); + var imgShape = new ImageShape({ + id: id, + style: { + x: 0, + y: 0, + image: canvas + } + }); + + if (pathTransform.position != null) { + imgShape.position = path.position = pathTransform.position; + } + + if (pathTransform.rotation != null) { + imgShape.rotation = path.rotation = pathTransform.rotation; + } + + if (pathTransform.scale != null) { + imgShape.scale = path.scale = pathTransform.scale; + } + + return imgShape; + }, + + _createPathToImage: function () { + var me = this; + + return function (id, e, width, height) { + return me._pathToImage( + id, e, width, height, me.dpr + ); + }; + } + }; + + module.exports = Painter; + + + +/***/ }, +/* 92 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module zrender/Layer + * @author pissang(https://www.github.com/pissang) + */ + + + var util = __webpack_require__(4); + var config = __webpack_require__(41); + var Style = __webpack_require__(47); + var Pattern = __webpack_require__(59); + + function returnFalse() { + return false; + } + + /** + * 创建dom + * + * @inner + * @param {string} id dom id 待用 + * @param {string} type dom type,such as canvas, div etc. + * @param {Painter} painter painter instance + * @param {number} number + */ + function createDom(id, type, painter, dpr) { + var newDom = document.createElement(type); + var width = painter.getWidth(); + var height = painter.getHeight(); + + var newDomStyle = newDom.style; + // 没append呢,请原谅我这样写,清晰~ + newDomStyle.position = 'absolute'; + newDomStyle.left = 0; + newDomStyle.top = 0; + newDomStyle.width = width + 'px'; + newDomStyle.height = height + 'px'; + newDom.width = width * dpr; + newDom.height = height * dpr; + + // id不作为索引用,避免可能造成的重名,定义为私有属性 + newDom.setAttribute('data-zr-dom-id', id); + return newDom; + } + + /** + * @alias module:zrender/Layer + * @constructor + * @extends module:zrender/mixin/Transformable + * @param {string} id + * @param {module:zrender/Painter} painter + * @param {number} [dpr] + */ + var Layer = function(id, painter, dpr) { + var dom; + dpr = dpr || config.devicePixelRatio; + if (typeof id === 'string') { + dom = createDom(id, 'canvas', painter, dpr); + } + // Not using isDom because in node it will return false + else if (util.isObject(id)) { + dom = id; + id = dom.id; + } + this.id = id; + this.dom = dom; + + var domStyle = dom.style; + if (domStyle) { // Not in node + dom.onselectstart = returnFalse; // 避免页面选中的尴尬 + domStyle['-webkit-user-select'] = 'none'; + domStyle['user-select'] = 'none'; + domStyle['-webkit-touch-callout'] = 'none'; + domStyle['-webkit-tap-highlight-color'] = 'rgba(0,0,0,0)'; + } + + this.domBack = null; + this.ctxBack = null; + + this.painter = painter; + + this.config = null; + + // Configs + /** + * 每次清空画布的颜色 + * @type {string} + * @default 0 + */ + this.clearColor = 0; + /** + * 是否开启动态模糊 + * @type {boolean} + * @default false + */ + this.motionBlur = false; + /** + * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显 + * @type {number} + * @default 0.7 + */ + this.lastFrameAlpha = 0.7; + + /** + * Layer dpr + * @type {number} + */ + this.dpr = dpr; + }; + + Layer.prototype = { + + constructor: Layer, + + elCount: 0, + + __dirty: true, + + initContext: function () { + this.ctx = this.dom.getContext('2d'); + + this.ctx.dpr = this.dpr; + }, + + createBackBuffer: function () { + var dpr = this.dpr; + + this.domBack = createDom('back-' + this.id, 'canvas', this.painter, dpr); + this.ctxBack = this.domBack.getContext('2d'); + + if (dpr != 1) { + this.ctxBack.scale(dpr, dpr); + } + }, + + /** + * @param {number} width + * @param {number} height + */ + resize: function (width, height) { + var dpr = this.dpr; + + var dom = this.dom; + var domStyle = dom.style; + var domBack = this.domBack; + + domStyle.width = width + 'px'; + domStyle.height = height + 'px'; + + dom.width = width * dpr; + dom.height = height * dpr; + + if (domBack) { + domBack.width = width * dpr; + domBack.height = height * dpr; + + if (dpr != 1) { + this.ctxBack.scale(dpr, dpr); + } + } + }, + + /** + * 清空该层画布 + * @param {boolean} clearAll Clear all with out motion blur + */ + clear: function (clearAll) { + var dom = this.dom; + var ctx = this.ctx; + var width = dom.width; + var height = dom.height; + + var clearColor = this.clearColor; + var haveMotionBLur = this.motionBlur && !clearAll; + var lastFrameAlpha = this.lastFrameAlpha; + + var dpr = this.dpr; + + if (haveMotionBLur) { + if (!this.domBack) { + this.createBackBuffer(); + } + + this.ctxBack.globalCompositeOperation = 'copy'; + this.ctxBack.drawImage( + dom, 0, 0, + width / dpr, + height / dpr + ); + } + + ctx.clearRect(0, 0, width, height); + if (clearColor) { + var clearColorGradientOrPattern; + // Gradient + if (clearColor.colorStops) { + // Cache canvas gradient + clearColorGradientOrPattern = clearColor.__canvasGradient || Style.getGradient(ctx, clearColor, { + x: 0, + y: 0, + width: width, + height: height + }); + + clearColor.__canvasGradient = clearColorGradientOrPattern; + } + // Pattern + else if (clearColor.image) { + clearColorGradientOrPattern = Pattern.prototype.getCanvasPattern.call(clearColor, ctx); + } + ctx.save(); + ctx.fillStyle = clearColorGradientOrPattern || clearColor; + ctx.fillRect(0, 0, width, height); + ctx.restore(); + } + + if (haveMotionBLur) { + var domBack = this.domBack; + ctx.save(); + ctx.globalAlpha = lastFrameAlpha; + ctx.drawImage(domBack, 0, 0, width, height); + ctx.restore(); + } + } + }; + + module.exports = Layer; + + +/***/ }, +/* 93 */ +/***/ function(module, exports, __webpack_require__) { + + + var Gradient = __webpack_require__(61); + module.exports = function (ecModel) { + function encodeColor(seriesModel) { + var colorAccessPath = (seriesModel.visualColorAccessPath || 'itemStyle.normal.color').split('.'); + var data = seriesModel.getData(); + var color = seriesModel.get(colorAccessPath) // Set in itemStyle + || seriesModel.getColorFromPalette(seriesModel.get('name')); // Default color + + // FIXME Set color function or use the platte color + data.setVisual('color', color); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + if (typeof color === 'function' && !(color instanceof Gradient)) { + data.each(function (idx) { + data.setItemVisual( + idx, 'color', color(seriesModel.getDataParams(idx)) + ); + }); + } + + // itemStyle in each data item + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var color = itemModel.get(colorAccessPath, true); + if (color != null) { + data.setItemVisual(idx, 'color', color); + } + }); + } + } + ecModel.eachRawSeries(encodeColor); + }; + + +/***/ }, +/* 94 */ +/***/ function(module, exports, __webpack_require__) { + + // Compatitable with 2.0 + + + var zrUtil = __webpack_require__(4); + var compatStyle = __webpack_require__(95); + + function get(opt, path) { + path = path.split(','); + var obj = opt; + for (var i = 0; i < path.length; i++) { + obj = obj && obj[path[i]]; + if (obj == null) { + break; + } + } + return obj; + } + + function set(opt, path, val, overwrite) { + path = path.split(','); + var obj = opt; + var key; + for (var i = 0; i < path.length - 1; i++) { + key = path[i]; + if (obj[key] == null) { + obj[key] = {}; + } + obj = obj[key]; + } + if (overwrite || obj[path[i]] == null) { + obj[path[i]] = val; + } + } + + function compatLayoutProperties(option) { + each(LAYOUT_PROPERTIES, function (prop) { + if (prop[0] in option && !(prop[1] in option)) { + option[prop[1]] = option[prop[0]]; + } + }); + } + + var LAYOUT_PROPERTIES = [ + ['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom'] + ]; + + var COMPATITABLE_COMPONENTS = [ + 'grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline' + ]; + + var COMPATITABLE_SERIES = [ + 'bar', 'boxplot', 'candlestick', 'chord', 'effectScatter', + 'funnel', 'gauge', 'lines', 'graph', 'heatmap', 'line', 'map', 'parallel', + 'pie', 'radar', 'sankey', 'scatter', 'treemap' + ]; + + var each = zrUtil.each; + + module.exports = function (option) { + each(option.series, function (seriesOpt) { + if (!zrUtil.isObject(seriesOpt)) { + return; + } + + var seriesType = seriesOpt.type; + + compatStyle(seriesOpt); + + if (seriesType === 'pie' || seriesType === 'gauge') { + if (seriesOpt.clockWise != null) { + seriesOpt.clockwise = seriesOpt.clockWise; + } + } + if (seriesType === 'gauge') { + var pointerColor = get(seriesOpt, 'pointer.color'); + pointerColor != null + && set(seriesOpt, 'itemStyle.normal.color', pointerColor); + } + + for (var i = 0; i < COMPATITABLE_SERIES.length; i++) { + if (COMPATITABLE_SERIES[i] === seriesOpt.type) { + compatLayoutProperties(seriesOpt); + break; + } + } + }); + + // dataRange has changed to visualMap + if (option.dataRange) { + option.visualMap = option.dataRange; + } + + each(COMPATITABLE_COMPONENTS, function (componentName) { + var options = option[componentName]; + if (options) { + if (!zrUtil.isArray(options)) { + options = [options]; + } + each(options, function (option) { + compatLayoutProperties(option); + }); + } + }); + }; + + +/***/ }, +/* 95 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var POSSIBLE_STYLES = [ + 'areaStyle', 'lineStyle', 'nodeStyle', 'linkStyle', + 'chordStyle', 'label', 'labelLine' + ]; + + function compatItemStyle(opt) { + var itemStyleOpt = opt && opt.itemStyle; + if (itemStyleOpt) { + zrUtil.each(POSSIBLE_STYLES, function (styleName) { + var normalItemStyleOpt = itemStyleOpt.normal; + var emphasisItemStyleOpt = itemStyleOpt.emphasis; + if (normalItemStyleOpt && normalItemStyleOpt[styleName]) { + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].normal) { + opt[styleName].normal = normalItemStyleOpt[styleName]; + } + else { + zrUtil.merge(opt[styleName].normal, normalItemStyleOpt[styleName]); + } + normalItemStyleOpt[styleName] = null; + } + if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) { + opt[styleName] = opt[styleName] || {}; + if (!opt[styleName].emphasis) { + opt[styleName].emphasis = emphasisItemStyleOpt[styleName]; + } + else { + zrUtil.merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); + } + emphasisItemStyleOpt[styleName] = null; + } + }); + } + } + + module.exports = function (seriesOpt) { + if (!seriesOpt) { + return; + } + compatItemStyle(seriesOpt); + compatItemStyle(seriesOpt.markPoint); + compatItemStyle(seriesOpt.markLine); + var data = seriesOpt.data; + if (data) { + for (var i = 0; i < data.length; i++) { + compatItemStyle(data[i]); + } + // mark point data + var markPoint = seriesOpt.markPoint; + if (markPoint && markPoint.data) { + var mpData = markPoint.data; + for (var i = 0; i < mpData.length; i++) { + compatItemStyle(mpData[i]); + } + } + // mark line data + var markLine = seriesOpt.markLine; + if (markLine && markLine.data) { + var mlData = markLine.data; + for (var i = 0; i < mlData.length; i++) { + if (zrUtil.isArray(mlData[i])) { + compatItemStyle(mlData[i][0]); + compatItemStyle(mlData[i][1]); + } + else { + compatItemStyle(mlData[i]); + } + } + } + } + }; + + +/***/ }, +/* 96 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var PI = Math.PI; + /** + * @param {module:echarts/ExtensionAPI} api + * @param {Object} [opts] + * @param {string} [opts.text] + * @param {string} [opts.color] + * @param {string} [opts.textColor] + * @return {module:zrender/Element} + */ + module.exports = function (api, opts) { + opts = opts || {}; + zrUtil.defaults(opts, { + text: 'loading', + color: '#c23531', + textColor: '#000', + maskColor: 'rgba(255, 255, 255, 0.8)', + zlevel: 0 + }); + var mask = new graphic.Rect({ + style: { + fill: opts.maskColor + }, + zlevel: opts.zlevel, + z: 10000 + }); + var arc = new graphic.Arc({ + shape: { + startAngle: -PI / 2, + endAngle: -PI / 2 + 0.1, + r: 10 + }, + style: { + stroke: opts.color, + lineCap: 'round', + lineWidth: 5 + }, + zlevel: opts.zlevel, + z: 10001 + }); + var labelRect = new graphic.Rect({ + style: { + fill: 'none', + text: opts.text, + textPosition: 'right', + textDistance: 10, + textFill: opts.textColor + }, + zlevel: opts.zlevel, + z: 10001 + }); + + arc.animateShape(true) + .when(1000, { + endAngle: PI * 3 / 2 + }) + .start('circularInOut'); + arc.animateShape(true) + .when(1000, { + startAngle: PI * 3 / 2 + }) + .delay(300) + .start('circularInOut'); + + var group = new graphic.Group(); + group.add(arc); + group.add(labelRect); + group.add(mask); + // Inject resize + group.resize = function () { + var cx = api.getWidth() / 2; + var cy = api.getHeight() / 2; + arc.setShape({ + cx: cx, + cy: cy + }); + var r = arc.shape.r; + labelRect.setShape({ + x: cx - r, + y: cy - r, + width: r * 2, + height: r * 2 + }); + + mask.setShape({ + x: 0, + y: 0, + width: api.getWidth(), + height: api.getHeight() + }); + }; + group.resize(); + return group; + }; + + +/***/ }, +/* 97 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(global) {/** + * List for data storage + * @module echarts/data/List + */ + + + var UNDEFINED = 'undefined'; + var globalObj = typeof window === 'undefined' ? global : window; + var Float64Array = typeof globalObj.Float64Array === UNDEFINED + ? Array : globalObj.Float64Array; + var Int32Array = typeof globalObj.Int32Array === UNDEFINED + ? Array : globalObj.Int32Array; + + var dataCtors = { + 'float': Float64Array, + 'int': Int32Array, + // Ordinal data type can be string or int + 'ordinal': Array, + 'number': Array, + 'time': Array + }; + + var Model = __webpack_require__(12); + var DataDiffer = __webpack_require__(98); + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var isObject = zrUtil.isObject; + + var TRANSFERABLE_PROPERTIES = [ + 'stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData' + ]; + + var transferProperties = function (a, b) { + zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) { + if (b.hasOwnProperty(propName)) { + a[propName] = b[propName]; + } + }); + + a.__wrappedMethods = b.__wrappedMethods; + }; + + /** + * @constructor + * @alias module:echarts/data/List + * + * @param {Array.} dimensions + * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius + * @param {module:echarts/model/Model} hostModel + */ + var List = function (dimensions, hostModel) { + + dimensions = dimensions || ['x', 'y']; + + var dimensionInfos = {}; + var dimensionNames = []; + for (var i = 0; i < dimensions.length; i++) { + var dimensionName; + var dimensionInfo = {}; + if (typeof dimensions[i] === 'string') { + dimensionName = dimensions[i]; + dimensionInfo = { + name: dimensionName, + stackable: false, + // Type can be 'float', 'int', 'number' + // Default is number, Precision of float may not enough + type: 'number' + }; + } + else { + dimensionInfo = dimensions[i]; + dimensionName = dimensionInfo.name; + dimensionInfo.type = dimensionInfo.type || 'number'; + } + dimensionNames.push(dimensionName); + dimensionInfos[dimensionName] = dimensionInfo; + } + /** + * @readOnly + * @type {Array.} + */ + this.dimensions = dimensionNames; + + /** + * Infomation of each data dimension, like data type. + * @type {Object} + */ + this._dimensionInfos = dimensionInfos; + + /** + * @type {module:echarts/model/Model} + */ + this.hostModel = hostModel; + + /** + * @type {module:echarts/model/Model} + */ + this.dataType; + + /** + * Indices stores the indices of data subset after filtered. + * This data subset will be used in chart. + * @type {Array.} + * @readOnly + */ + this.indices = []; + + /** + * Data storage + * @type {Object.} + * @private + */ + this._storage = {}; + + /** + * @type {Array.} + */ + this._nameList = []; + /** + * @type {Array.} + */ + this._idList = []; + /** + * Models of data option is stored sparse for optimizing memory cost + * @type {Array.} + * @private + */ + this._optionModels = []; + + /** + * @param {module:echarts/data/List} + */ + this.stackedOn = null; + + /** + * Global visual properties after visual coding + * @type {Object} + * @private + */ + this._visual = {}; + + /** + * Globel layout properties. + * @type {Object} + * @private + */ + this._layout = {}; + + /** + * Item visual properties after visual coding + * @type {Array.} + * @private + */ + this._itemVisuals = []; + + /** + * Item layout properties after layout + * @type {Array.} + * @private + */ + this._itemLayouts = []; + + /** + * Graphic elemnents + * @type {Array.} + * @private + */ + this._graphicEls = []; + + /** + * @type {Array.} + * @private + */ + this._rawData; + + /** + * @type {Object} + * @private + */ + this._extent; + }; + + var listProto = List.prototype; + + listProto.type = 'list'; + /** + * If each data item has it's own option + * @type {boolean} + */ + listProto.hasItemOption = true; + + /** + * Get dimension name + * @param {string|number} dim + * Dimension can be concrete names like x, y, z, lng, lat, angle, radius + * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' + * @return {string} Concrete dim name. + */ + listProto.getDimension = function (dim) { + if (!isNaN(dim)) { + dim = this.dimensions[dim] || dim; + } + return dim; + }; + /** + * Get type and stackable info of particular dimension + * @param {string|number} dim + * Dimension can be concrete names like x, y, z, lng, lat, angle, radius + * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' + */ + listProto.getDimensionInfo = function (dim) { + return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]); + }; + + /** + * Initialize from data + * @param {Array.} data + * @param {Array.} [nameList] + * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number + */ + listProto.initData = function (data, nameList, dimValueGetter) { + data = data || []; + + if (true) { + if (!zrUtil.isArray(data)) { + throw new Error('Invalid data.'); + } + } + + this._rawData = data; + + // Clear + var storage = this._storage = {}; + var indices = this.indices = []; + + var dimensions = this.dimensions; + var size = data.length; + var dimensionInfoMap = this._dimensionInfos; + + var idList = []; + var nameRepeatCount = {}; + + nameList = nameList || []; + + // Init storage + for (var i = 0; i < dimensions.length; i++) { + var dimInfo = dimensionInfoMap[dimensions[i]]; + var DataCtor = dataCtors[dimInfo.type]; + storage[dimensions[i]] = new DataCtor(size); + } + + var self = this; + if (!dimValueGetter) { + self.hasItemOption = false; + } + // Default dim value getter + dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) { + var value = modelUtil.getDataItemValue(dataItem); + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(dataItem)) { + self.hasItemOption = true; + } + return modelUtil.converDataValue( + (value instanceof Array) + ? value[dimIndex] + // If value is a single number or something else not array. + : value, + dimensionInfoMap[dimName] + ); + }; + + for (var idx = 0; idx < data.length; idx++) { + var dataItem = data[idx]; + // Each data item is value + // [1, 2] + // 2 + // Bar chart, line chart which uses category axis + // only gives the 'y' value. 'x' value is the indices of cateogry + // Use a tempValue to normalize the value to be a (x, y) value + + // Store the data by dimensions + for (var k = 0; k < dimensions.length; k++) { + var dim = dimensions[k]; + var dimStorage = storage[dim]; + // PENDING NULL is empty or zero + dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k); + } + + indices.push(idx); + } + + // Use the name in option and create id + for (var i = 0; i < data.length; i++) { + if (!nameList[i]) { + if (data[i] && data[i].name != null) { + nameList[i] = data[i].name; + } + } + var name = nameList[i] || ''; + // Try using the id in option + var id = data[i] && data[i].id; + + if (!id && name) { + // Use name as id and add counter to avoid same name + nameRepeatCount[name] = nameRepeatCount[name] || 0; + id = name; + if (nameRepeatCount[name] > 0) { + id += '__ec__' + nameRepeatCount[name]; + } + nameRepeatCount[name]++; + } + id && (idList[i] = id); + } + + this._nameList = nameList; + this._idList = idList; + }; + + /** + * @return {number} + */ + listProto.count = function () { + return this.indices.length; + }; + + /** + * Get value. Return NaN if idx is out of range. + * @param {string} dim Dim must be concrete name. + * @param {number} idx + * @param {boolean} stack + * @return {number} + */ + listProto.get = function (dim, idx, stack) { + var storage = this._storage; + var dataIndex = this.indices[idx]; + + // If value not exists + if (dataIndex == null) { + return NaN; + } + + var value = storage[dim] && storage[dim][dataIndex]; + // FIXME ordinal data type is not stackable + if (stack) { + var dimensionInfo = this._dimensionInfos[dim]; + if (dimensionInfo && dimensionInfo.stackable) { + var stackedOn = this.stackedOn; + while (stackedOn) { + // Get no stacked data of stacked on + var stackedValue = stackedOn.get(dim, idx); + // Considering positive stack, negative stack and empty data + if ((value >= 0 && stackedValue > 0) // Positive stack + || (value <= 0 && stackedValue < 0) // Negative stack + ) { + value += stackedValue; + } + stackedOn = stackedOn.stackedOn; + } + } + } + return value; + }; + + /** + * Get value for multi dimensions. + * @param {Array.} [dimensions] If ignored, using all dimensions. + * @param {number} idx + * @param {boolean} stack + * @return {number} + */ + listProto.getValues = function (dimensions, idx, stack) { + var values = []; + + if (!zrUtil.isArray(dimensions)) { + stack = idx; + idx = dimensions; + dimensions = this.dimensions; + } + + for (var i = 0, len = dimensions.length; i < len; i++) { + values.push(this.get(dimensions[i], idx, stack)); + } + + return values; + }; + + /** + * If value is NaN. Inlcuding '-' + * @param {string} dim + * @param {number} idx + * @return {number} + */ + listProto.hasValue = function (idx) { + var dimensions = this.dimensions; + var dimensionInfos = this._dimensionInfos; + for (var i = 0, len = dimensions.length; i < len; i++) { + if ( + // Ordinal type can be string or number + dimensionInfos[dimensions[i]].type !== 'ordinal' + && isNaN(this.get(dimensions[i], idx)) + ) { + return false; + } + } + return true; + }; + + /** + * Get extent of data in one dimension + * @param {string} dim + * @param {boolean} stack + */ + listProto.getDataExtent = function (dim, stack) { + dim = this.getDimension(dim); + var dimData = this._storage[dim]; + var dimInfo = this.getDimensionInfo(dim); + stack = (dimInfo && dimInfo.stackable) && stack; + var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)]; + var value; + if (dimExtent) { + return dimExtent; + } + // var dimInfo = this._dimensionInfos[dim]; + if (dimData) { + var min = Infinity; + var max = -Infinity; + // var isOrdinal = dimInfo.type === 'ordinal'; + for (var i = 0, len = this.count(); i < len; i++) { + value = this.get(dim, i, stack); + // FIXME + // if (isOrdinal && typeof value === 'string') { + // value = zrUtil.indexOf(dimData, value); + // } + value < min && (min = value); + value > max && (max = value); + } + return (this._extent[dim + !!stack] = [min, max]); + } + else { + return [Infinity, -Infinity]; + } + }; + + /** + * Get sum of data in one dimension + * @param {string} dim + * @param {boolean} stack + */ + listProto.getSum = function (dim, stack) { + var dimData = this._storage[dim]; + var sum = 0; + if (dimData) { + for (var i = 0, len = this.count(); i < len; i++) { + var value = this.get(dim, i, stack); + if (!isNaN(value)) { + sum += value; + } + } + } + return sum; + }; + + /** + * Retreive the index with given value + * @param {number} idx + * @param {number} value + * @return {number} + */ + // FIXME Precision of float value + listProto.indexOf = function (dim, value) { + var storage = this._storage; + var dimData = storage[dim]; + var indices = this.indices; + + if (dimData) { + for (var i = 0, len = indices.length; i < len; i++) { + var rawIndex = indices[i]; + if (dimData[rawIndex] === value) { + return i; + } + } + } + return -1; + }; + + /** + * Retreive the index with given name + * @param {number} idx + * @param {number} name + * @return {number} + */ + listProto.indexOfName = function (name) { + var indices = this.indices; + var nameList = this._nameList; + + for (var i = 0, len = indices.length; i < len; i++) { + var rawIndex = indices[i]; + if (nameList[rawIndex] === name) { + return i; + } + } + + return -1; + }; + + /** + * Retreive the index with given raw data index + * @param {number} idx + * @param {number} name + * @return {number} + */ + listProto.indexOfRawIndex = function (rawIndex) { + // Indices are ascending + var indices = this.indices; + var left = 0; + var right = indices.length - 1; + while (left <= right) { + var mid = (left + right) / 2 | 0; + if (indices[mid] < rawIndex) { + left = mid + 1; + } + else if (indices[mid] > rawIndex) { + right = mid - 1; + } + else { + return mid; + } + } + return -1; + }; + + /** + * Retreive the index of nearest value + * @param {string} dim + * @param {number} value + * @param {boolean} stack If given value is after stacked + * @param {number} [maxDistance=Infinity] + * @return {number} + */ + listProto.indexOfNearest = function (dim, value, stack, maxDistance) { + var storage = this._storage; + var dimData = storage[dim]; + + if (maxDistance == null) { + maxDistance = Infinity; + } + + var nearestIdx = -1; + if (dimData) { + var minDist = Number.MAX_VALUE; + for (var i = 0, len = this.count(); i < len; i++) { + var diff = value - this.get(dim, i, stack); + var dist = Math.abs(diff); + if ( + diff <= maxDistance + && (dist < minDist + // For the case of two data are same on xAxis, which has sequence data. + // Show the nearest index + // https://github.com/ecomfe/echarts/issues/2869 + || (dist === minDist && diff > 0) + ) + ) { + minDist = dist; + nearestIdx = i; + } + } + } + return nearestIdx; + }; + + /** + * Get raw data index + * @param {number} idx + * @return {number} + */ + listProto.getRawIndex = function (idx) { + var rawIdx = this.indices[idx]; + return rawIdx == null ? -1 : rawIdx; + }; + + /** + * Get raw data item + * @param {number} idx + * @return {number} + */ + listProto.getRawDataItem = function (idx) { + return this._rawData[this.getRawIndex(idx)]; + }; + + /** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ + listProto.getName = function (idx) { + return this._nameList[this.indices[idx]] || ''; + }; + + /** + * @param {number} idx + * @param {boolean} [notDefaultIdx=false] + * @return {string} + */ + listProto.getId = function (idx) { + return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + ''); + }; + + + function normalizeDimensions(dimensions) { + if (!zrUtil.isArray(dimensions)) { + dimensions = [dimensions]; + } + return dimensions; + } + + /** + * Data iteration + * @param {string|Array.} + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * + * @example + * list.each('x', function (x, idx) {}); + * list.each(['x', 'y'], function (x, y, idx) {}); + * list.each(function (idx) {}) + */ + listProto.each = function (dims, cb, stack, context) { + if (typeof dims === 'function') { + context = stack; + stack = cb; + cb = dims; + dims = []; + } + + dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this); + + var value = []; + var dimSize = dims.length; + var indices = this.indices; + + context = context || this; + + for (var i = 0; i < indices.length; i++) { + // Simple optimization + switch (dimSize) { + case 0: + cb.call(context, i); + break; + case 1: + cb.call(context, this.get(dims[0], i, stack), i); + break; + case 2: + cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i); + break; + default: + for (var k = 0; k < dimSize; k++) { + value[k] = this.get(dims[k], i, stack); + } + // Index + value[k] = i; + cb.apply(context, value); + } + } + }; + + /** + * Data filter + * @param {string|Array.} + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + */ + listProto.filterSelf = function (dimensions, cb, stack, context) { + if (typeof dimensions === 'function') { + context = stack; + stack = cb; + cb = dimensions; + dimensions = []; + } + + dimensions = zrUtil.map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + var newIndices = []; + var value = []; + var dimSize = dimensions.length; + var indices = this.indices; + + context = context || this; + + for (var i = 0; i < indices.length; i++) { + var keep; + // Simple optimization + if (dimSize === 1) { + keep = cb.call( + context, this.get(dimensions[0], i, stack), i + ); + } + else { + for (var k = 0; k < dimSize; k++) { + value[k] = this.get(dimensions[k], i, stack); + } + value[k] = i; + keep = cb.apply(context, value); + } + if (keep) { + newIndices.push(indices[i]); + } + } + + this.indices = newIndices; + + // Reset data extent + this._extent = {}; + + return this; + }; + + /** + * Data mapping to a plain array + * @param {string|Array.} [dimensions] + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * @return {Array} + */ + listProto.mapArray = function (dimensions, cb, stack, context) { + if (typeof dimensions === 'function') { + context = stack; + stack = cb; + cb = dimensions; + dimensions = []; + } + + var result = []; + this.each(dimensions, function () { + result.push(cb && cb.apply(this, arguments)); + }, stack, context); + return result; + }; + + function cloneListForMapAndSample(original, excludeDimensions) { + var allDimensions = original.dimensions; + var list = new List( + zrUtil.map(allDimensions, original.getDimensionInfo, original), + original.hostModel + ); + // FIXME If needs stackedOn, value may already been stacked + transferProperties(list, original); + + var storage = list._storage = {}; + var originalStorage = original._storage; + // Init storage + for (var i = 0; i < allDimensions.length; i++) { + var dim = allDimensions[i]; + var dimStore = originalStorage[dim]; + if (zrUtil.indexOf(excludeDimensions, dim) >= 0) { + storage[dim] = new dimStore.constructor( + originalStorage[dim].length + ); + } + else { + // Direct reference for other dimensions + storage[dim] = originalStorage[dim]; + } + } + return list; + } + + /** + * Data mapping to a new List with given dimensions + * @param {string|Array.} dimensions + * @param {Function} cb + * @param {boolean} [stack=false] + * @param {*} [context=this] + * @return {Array} + */ + listProto.map = function (dimensions, cb, stack, context) { + dimensions = zrUtil.map( + normalizeDimensions(dimensions), this.getDimension, this + ); + + var list = cloneListForMapAndSample(this, dimensions); + // Following properties are all immutable. + // So we can reference to the same value + var indices = list.indices = this.indices; + + var storage = list._storage; + + var tmpRetValue = []; + this.each(dimensions, function () { + var idx = arguments[arguments.length - 1]; + var retValue = cb && cb.apply(this, arguments); + if (retValue != null) { + // a number + if (typeof retValue === 'number') { + tmpRetValue[0] = retValue; + retValue = tmpRetValue; + } + for (var i = 0; i < retValue.length; i++) { + var dim = dimensions[i]; + var dimStore = storage[dim]; + var rawIdx = indices[idx]; + if (dimStore) { + dimStore[rawIdx] = retValue[i]; + } + } + } + }, stack, context); + + return list; + }; + + /** + * Large data down sampling on given dimension + * @param {string} dimension + * @param {number} rate + * @param {Function} sampleValue + * @param {Function} sampleIndex Sample index for name and id + */ + listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) { + var list = cloneListForMapAndSample(this, [dimension]); + var storage = this._storage; + var targetStorage = list._storage; + + var originalIndices = this.indices; + var indices = list.indices = []; + + var frameValues = []; + var frameIndices = []; + var frameSize = Math.floor(1 / rate); + + var dimStore = targetStorage[dimension]; + var len = this.count(); + // Copy data from original data + for (var i = 0; i < storage[dimension].length; i++) { + targetStorage[dimension][i] = storage[dimension][i]; + } + for (var i = 0; i < len; i += frameSize) { + // Last frame + if (frameSize > len - i) { + frameSize = len - i; + frameValues.length = frameSize; + } + for (var k = 0; k < frameSize; k++) { + var idx = originalIndices[i + k]; + frameValues[k] = dimStore[idx]; + frameIndices[k] = idx; + } + var value = sampleValue(frameValues); + var idx = frameIndices[sampleIndex(frameValues, value) || 0]; + // Only write value on the filtered data + dimStore[idx] = value; + indices.push(idx); + } + + return list; + }; + + /** + * Get model of one data item. + * + * @param {number} idx + */ + // FIXME Model proxy ? + listProto.getItemModel = function (idx) { + var hostModel = this.hostModel; + idx = this.indices[idx]; + return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel); + }; + + /** + * Create a data differ + * @param {module:echarts/data/List} otherList + * @return {module:echarts/data/DataDiffer} + */ + listProto.diff = function (otherList) { + var idList = this._idList; + var otherIdList = otherList && otherList._idList; + return new DataDiffer( + otherList ? otherList.indices : [], this.indices, function (idx) { + return otherIdList[idx] || (idx + ''); + }, function (idx) { + return idList[idx] || (idx + ''); + } + ); + }; + /** + * Get visual property. + * @param {string} key + */ + listProto.getVisual = function (key) { + var visual = this._visual; + return visual && visual[key]; + }; + + /** + * Set visual property + * @param {string|Object} key + * @param {*} [value] + * + * @example + * setVisual('color', color); + * setVisual({ + * 'color': color + * }); + */ + listProto.setVisual = function (key, val) { + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.setVisual(name, key[name]); + } + } + return; + } + this._visual = this._visual || {}; + this._visual[key] = val; + }; + + /** + * Set layout property. + * @param {string} key + * @param {*} [val] + */ + listProto.setLayout = function (key, val) { + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + this.setLayout(name, key[name]); + } + } + return; + } + this._layout[key] = val; + }; + + /** + * Get layout property. + * @param {string} key. + * @return {*} + */ + listProto.getLayout = function (key) { + return this._layout[key]; + }; + + /** + * Get layout of single data item + * @param {number} idx + */ + listProto.getItemLayout = function (idx) { + return this._itemLayouts[idx]; + }; + + /** + * Set layout of single data item + * @param {number} idx + * @param {Object} layout + * @param {boolean=} [merge=false] + */ + listProto.setItemLayout = function (idx, layout, merge) { + this._itemLayouts[idx] = merge + ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) + : layout; + }; + + /** + * Clear all layout of single data item + */ + listProto.clearItemLayouts = function () { + this._itemLayouts.length = 0; + }; + + /** + * Get visual property of single data item + * @param {number} idx + * @param {string} key + * @param {boolean} ignoreParent + */ + listProto.getItemVisual = function (idx, key, ignoreParent) { + var itemVisual = this._itemVisuals[idx]; + var val = itemVisual && itemVisual[key]; + if (val == null && !ignoreParent) { + // Use global visual property + return this.getVisual(key); + } + return val; + }; + + /** + * Set visual property of single data item + * + * @param {number} idx + * @param {string|Object} key + * @param {*} [value] + * + * @example + * setItemVisual(0, 'color', color); + * setItemVisual(0, { + * 'color': color + * }); + */ + listProto.setItemVisual = function (idx, key, value) { + var itemVisual = this._itemVisuals[idx] || {}; + this._itemVisuals[idx] = itemVisual; + + if (isObject(key)) { + for (var name in key) { + if (key.hasOwnProperty(name)) { + itemVisual[name] = key[name]; + } + } + return; + } + itemVisual[key] = value; + }; + + /** + * Clear itemVisuals and list visual. + */ + listProto.clearAllVisual = function () { + this._visual = {}; + this._itemVisuals = []; + }; + + var setItemDataAndSeriesIndex = function (child) { + child.seriesIndex = this.seriesIndex; + child.dataIndex = this.dataIndex; + child.dataType = this.dataType; + }; + /** + * Set graphic element relative to data. It can be set as null + * @param {number} idx + * @param {module:zrender/Element} [el] + */ + listProto.setItemGraphicEl = function (idx, el) { + var hostModel = this.hostModel; + + if (el) { + // Add data index and series index for indexing the data by element + // Useful in tooltip + el.dataIndex = idx; + el.dataType = this.dataType; + el.seriesIndex = hostModel && hostModel.seriesIndex; + if (el.type === 'group') { + el.traverse(setItemDataAndSeriesIndex, el); + } + } + + this._graphicEls[idx] = el; + }; + + /** + * @param {number} idx + * @return {module:zrender/Element} + */ + listProto.getItemGraphicEl = function (idx) { + return this._graphicEls[idx]; + }; + + /** + * @param {Function} cb + * @param {*} context + */ + listProto.eachItemGraphicEl = function (cb, context) { + zrUtil.each(this._graphicEls, function (el, idx) { + if (el) { + cb && cb.call(context, el, idx); + } + }); + }; + + /** + * Shallow clone a new list except visual and layout properties, and graph elements. + * New list only change the indices. + */ + listProto.cloneShallow = function () { + var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this); + var list = new List(dimensionInfoList, this.hostModel); + + // FIXME + list._storage = this._storage; + + transferProperties(list, this); + + + // Clone will not change the data extent and indices + list.indices = this.indices.slice(); + + if (this._extent) { + list._extent = zrUtil.extend({}, this._extent); + } + + return list; + }; + + /** + * Wrap some method to add more feature + * @param {string} methodName + * @param {Function} injectFunction + */ + listProto.wrapMethod = function (methodName, injectFunction) { + var originalMethod = this[methodName]; + if (typeof originalMethod !== 'function') { + return; + } + this.__wrappedMethods = this.__wrappedMethods || []; + this.__wrappedMethods.push(methodName); + this[methodName] = function () { + var res = originalMethod.apply(this, arguments); + return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments))); + }; + }; + + // Methods that create a new list based on this list should be listed here. + // Notice that those method should `RETURN` the new list. + listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; + // Methods that change indices of this list should be listed here. + listProto.CHANGABLE_METHODS = ['filterSelf']; + + module.exports = List; + + /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) + +/***/ }, +/* 98 */ +/***/ function(module, exports) { + + 'use strict'; + + + function defaultKeyGetter(item) { + return item; + } + + function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter) { + this._old = oldArr; + this._new = newArr; + + this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; + this._newKeyGetter = newKeyGetter || defaultKeyGetter; + } + + DataDiffer.prototype = { + + constructor: DataDiffer, + + /** + * Callback function when add a data + */ + add: function (func) { + this._add = func; + return this; + }, + + /** + * Callback function when update a data + */ + update: function (func) { + this._update = func; + return this; + }, + + /** + * Callback function when remove a data + */ + remove: function (func) { + this._remove = func; + return this; + }, + + execute: function () { + var oldArr = this._old; + var newArr = this._new; + var oldKeyGetter = this._oldKeyGetter; + var newKeyGetter = this._newKeyGetter; + + var oldDataIndexMap = {}; + var newDataIndexMap = {}; + var oldDataKeyArr = []; + var newDataKeyArr = []; + var i; + + initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, oldKeyGetter); + initIndexMap(newArr, newDataIndexMap, newDataKeyArr, newKeyGetter); + + // Travel by inverted order to make sure order consistency + // when duplicate keys exists (consider newDataIndex.pop() below). + // For performance consideration, these code below do not look neat. + for (i = 0; i < oldArr.length; i++) { + var key = oldDataKeyArr[i]; + var idx = newDataIndexMap[key]; + + // idx can never be empty array here. see 'set null' logic below. + if (idx != null) { + // Consider there is duplicate key (for example, use dataItem.name as key). + // We should make sure every item in newArr and oldArr can be visited. + var len = idx.length; + if (len) { + len === 1 && (newDataIndexMap[key] = null); + idx = idx.unshift(); + } + else { + newDataIndexMap[key] = null; + } + this._update && this._update(idx, i); + } + else { + this._remove && this._remove(i); + } + } + + for (var i = 0; i < newDataKeyArr.length; i++) { + var key = newDataKeyArr[i]; + if (newDataIndexMap.hasOwnProperty(key)) { + var idx = newDataIndexMap[key]; + if (idx == null) { + continue; + } + // idx can never be empty array here. see 'set null' logic above. + if (!idx.length) { + this._add && this._add(idx); + } + else { + for (var j = 0, len = idx.length; j < len; j++) { + this._add && this._add(idx[j]); + } + } + } + } + } + }; + + function initIndexMap(arr, map, keyArr, keyGetter) { + for (var i = 0; i < arr.length; i++) { + var key = keyGetter(arr[i], i); + var existence = map[key]; + if (existence == null) { + keyArr.push(key); + map[key] = i; + } + else { + if (!existence.length) { + map[key] = existence = [existence]; + } + existence.push(i); + } + } + } + + module.exports = DataDiffer; + + +/***/ }, +/* 99 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + var PRIORITY = echarts.PRIORITY; + + __webpack_require__(100); + __webpack_require__(103); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'line', 'circle', 'line' + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'line' + )); + + // Down sample after filter + echarts.registerProcessor(PRIORITY.PROCESSOR.STATISTIC, zrUtil.curry( + __webpack_require__(111), 'line' + )); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 100 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.line', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + if (true) { + var coordSys = option.coordinateSystem; + if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { + throw new Error('Line not support coordinateSystem besides cartesian and polar'); + } + } + return createListFromArray(option.data, this, ecModel); + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + // stack: null + // xAxisIndex: 0, + // yAxisIndex: 0, + + // polarIndex: 0, + + // If clip the overflow value + clipOverflow: true, + + label: { + normal: { + position: 'top' + } + }, + // itemStyle: { + // normal: {}, + // emphasis: {} + // }, + lineStyle: { + normal: { + width: 2, + type: 'solid' + } + }, + // areaStyle: {}, + // false, 'start', 'end', 'middle' + step: false, + + // Disabled if step is true + smooth: false, + smoothMonotone: null, + // 拐点图形类型 + symbol: 'emptyCircle', + // 拐点图形大小 + symbolSize: 4, + // 拐点图形旋转控制 + symbolRotate: null, + + // 是否显示 symbol, 只有在 tooltip hover 的时候显示 + showSymbol: true, + // 标志图形默认只有主轴显示(随主轴标签间隔隐藏策略) + showAllSymbol: false, + + // 是否连接断点 + connectNulls: false, + + // 数据过滤,'average', 'max', 'min', 'sum' + sampling: 'none', + + animationEasing: 'linear', + + // Disable progressive + progressive: 0, + hoverLayerThreshold: Infinity + } + }); + + +/***/ }, +/* 101 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var CoordinateSystem = __webpack_require__(26); + var getDataItemValue = modelUtil.getDataItemValue; + var converDataValue = modelUtil.converDataValue; + + function firstDataNotNull(data) { + var i = 0; + while (i < data.length && data[i] == null) { + i++; + } + return data[i]; + } + function ifNeedCompleteOrdinalData(data) { + var sampleItem = firstDataNotNull(data); + return sampleItem != null + && !zrUtil.isArray(getDataItemValue(sampleItem)); + } + + /** + * Helper function to create a list from option data + */ + function createListFromArray(data, seriesModel, ecModel) { + // If data is undefined + data = data || []; + + if (true) { + if (!zrUtil.isArray(data)) { + throw new Error('Invalid data.'); + } + } + + var coordSysName = seriesModel.get('coordinateSystem'); + var creator = creators[coordSysName]; + var registeredCoordSys = CoordinateSystem.get(coordSysName); + // FIXME + var axesInfo = creator && creator(data, seriesModel, ecModel); + var dimensions = axesInfo && axesInfo.dimensions; + if (!dimensions) { + // Get dimensions from registered coordinate system + dimensions = (registeredCoordSys && registeredCoordSys.dimensions) || ['x', 'y']; + dimensions = completeDimensions(dimensions, data, dimensions.concat(['value'])); + } + var categoryIndex = axesInfo ? axesInfo.categoryIndex : -1; + + var list = new List(dimensions, seriesModel); + + var nameList = createNameList(axesInfo, data); + + var categories = {}; + var dimValueGetter = (categoryIndex >= 0 && ifNeedCompleteOrdinalData(data)) + ? function (itemOpt, dimName, dataIndex, dimIndex) { + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(itemOpt)) { + list.hasItemOption = true; + } + + // Use dataIndex as ordinal value in categoryAxis + return dimIndex === categoryIndex + ? dataIndex + : converDataValue(getDataItemValue(itemOpt), dimensions[dimIndex]); + } + : function (itemOpt, dimName, dataIndex, dimIndex) { + var value = getDataItemValue(itemOpt); + var val = converDataValue(value && value[dimIndex], dimensions[dimIndex]); + // If any dataItem is like { value: 10 } + if (modelUtil.isDataItemOption(itemOpt)) { + list.hasItemOption = true; + } + + var categoryAxesModels = axesInfo && axesInfo.categoryAxesModels; + if (categoryAxesModels && categoryAxesModels[dimName]) { + // If given value is a category string + if (typeof val === 'string') { + // Lazy get categories + categories[dimName] = categories[dimName] + || categoryAxesModels[dimName].getCategories(); + val = zrUtil.indexOf(categories[dimName], val); + if (val < 0 && !isNaN(val)) { + // In case some one write '1', '2' istead of 1, 2 + val = +val; + } + } + } + return val; + }; + + list.hasItemOption = false; + list.initData(data, nameList, dimValueGetter); + + return list; + } + + function isStackable(axisType) { + return axisType !== 'category' && axisType !== 'time'; + } + + function getDimTypeByAxis(axisType) { + return axisType === 'category' + ? 'ordinal' + : axisType === 'time' + ? 'time' + : 'float'; + } + + /** + * Creaters for each coord system. + */ + var creators = { + + cartesian2d: function (data, seriesModel, ecModel) { + + var axesModels = zrUtil.map(['xAxis', 'yAxis'], function (name) { + return ecModel.queryComponents({ + mainType: name, + index: seriesModel.get(name + 'Index'), + id: seriesModel.get(name + 'Id') + })[0]; + }); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + if (true) { + if (!xAxisModel) { + throw new Error('xAxis "' + zrUtil.retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('xAxisId'), + 0 + ) + '" not found'); + } + if (!yAxisModel) { + throw new Error('yAxis "' + zrUtil.retrieve( + seriesModel.get('xAxisIndex'), + seriesModel.get('yAxisId'), + 0 + ) + '" not found'); + } + } + + var xAxisType = xAxisModel.get('type'); + var yAxisType = yAxisModel.get('type'); + + var dimensions = [ + { + name: 'x', + type: getDimTypeByAxis(xAxisType), + stackable: isStackable(xAxisType) + }, + { + name: 'y', + // If two category axes + type: getDimTypeByAxis(yAxisType), + stackable: isStackable(yAxisType) + } + ]; + + var isXAxisCateogry = xAxisType === 'category'; + var isYAxisCategory = yAxisType === 'category'; + + completeDimensions(dimensions, data, ['x', 'y', 'z']); + + var categoryAxesModels = {}; + if (isXAxisCateogry) { + categoryAxesModels.x = xAxisModel; + } + if (isYAxisCategory) { + categoryAxesModels.y = yAxisModel; + } + return { + dimensions: dimensions, + categoryIndex: isXAxisCateogry ? 0 : (isYAxisCategory ? 1 : -1), + categoryAxesModels: categoryAxesModels + }; + }, + + polar: function (data, seriesModel, ecModel) { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: seriesModel.get('polarIndex'), + id: seriesModel.get('polarId') + })[0]; + + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + + if (true) { + if (!angleAxisModel) { + throw new Error('angleAxis option not found'); + } + if (!radiusAxisModel) { + throw new Error('radiusAxis option not found'); + } + } + + var radiusAxisType = radiusAxisModel.get('type'); + var angleAxisType = angleAxisModel.get('type'); + + var dimensions = [ + { + name: 'radius', + type: getDimTypeByAxis(radiusAxisType), + stackable: isStackable(radiusAxisType) + }, + { + name: 'angle', + type: getDimTypeByAxis(angleAxisType), + stackable: isStackable(angleAxisType) + } + ]; + var isAngleAxisCateogry = angleAxisType === 'category'; + var isRadiusAxisCateogry = radiusAxisType === 'category'; + + completeDimensions(dimensions, data, ['radius', 'angle', 'value']); + + var categoryAxesModels = {}; + if (isRadiusAxisCateogry) { + categoryAxesModels.radius = radiusAxisModel; + } + if (isAngleAxisCateogry) { + categoryAxesModels.angle = angleAxisModel; + } + return { + dimensions: dimensions, + categoryIndex: isAngleAxisCateogry ? 1 : (isRadiusAxisCateogry ? 0 : -1), + categoryAxesModels: categoryAxesModels + }; + }, + + geo: function (data, seriesModel, ecModel) { + // TODO Region + // 多个散点图系列在同一个地区的时候 + return { + dimensions: completeDimensions([ + {name: 'lng'}, + {name: 'lat'} + ], data, ['lng', 'lat', 'value']) + }; + } + }; + + function createNameList(result, data) { + var nameList = []; + + var categoryDim = result && result.dimensions[result.categoryIndex]; + var categoryAxisModel; + if (categoryDim) { + categoryAxisModel = result.categoryAxesModels[categoryDim.name]; + } + + if (categoryAxisModel) { + // FIXME Two category axis + var categories = categoryAxisModel.getCategories(); + if (categories) { + var dataLen = data.length; + // Ordered data is given explicitly like + // [[3, 0.2], [1, 0.3], [2, 0.15]] + // or given scatter data, + // pick the category + if (zrUtil.isArray(data[0]) && data[0].length > 1) { + nameList = []; + for (var i = 0; i < dataLen; i++) { + nameList[i] = categories[data[i][result.categoryIndex || 0]]; + } + } + else { + nameList = categories.slice(0); + } + } + } + + return nameList; + } + + module.exports = createListFromArray; + + + +/***/ }, +/* 102 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Complete dimensions by data (guess dimension). + */ + + + var zrUtil = __webpack_require__(4); + + /** + * Complete the dimensions array guessed from the data structure. + * @param {Array.} dimensions Necessary dimensions, like ['x', 'y'] + * @param {Array} data Data list. [[1, 2, 3], [2, 3, 4]] + * @param {Array.} defaultNames Default names to fill not necessary dimensions, like ['value'] + * @param {string} extraPrefix Prefix of name when filling the left dimensions. + * @return {Array.} + */ + function completeDimensions(dimensions, data, defaultNames, extraPrefix) { + if (!data) { + return dimensions; + } + + var value0 = retrieveValue(data[0]); + var dimSize = zrUtil.isArray(value0) && value0.length || 1; + + defaultNames = defaultNames || []; + extraPrefix = extraPrefix || 'extra'; + for (var i = 0; i < dimSize; i++) { + if (!dimensions[i]) { + var name = defaultNames[i] || (extraPrefix + (i - defaultNames.length)); + dimensions[i] = guessOrdinal(data, i) + ? {type: 'ordinal', name: name} + : name; + } + } + + return dimensions; + } + + // The rule should not be complex, otherwise user might not + // be able to known where the data is wrong. + var guessOrdinal = completeDimensions.guessOrdinal = function (data, dimIndex) { + for (var i = 0, len = data.length; i < len; i++) { + var value = retrieveValue(data[i]); + + if (!zrUtil.isArray(value)) { + return false; + } + + var value = value[dimIndex]; + if (value != null && isFinite(value)) { + return false; + } + else if (zrUtil.isString(value) && value !== '-') { + return true; + } + } + return false; + }; + + function retrieveValue(o) { + return zrUtil.isArray(o) ? o : zrUtil.isObject(o) ? o.value: o; + } + + module.exports = completeDimensions; + + + +/***/ }, +/* 103 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // FIXME step not support polar + + + var zrUtil = __webpack_require__(4); + var SymbolDraw = __webpack_require__(104); + var Symbol = __webpack_require__(105); + var lineAnimationDiff = __webpack_require__(107); + var graphic = __webpack_require__(43); + + var polyHelper = __webpack_require__(108); + + var ChartView = __webpack_require__(42); + + function isPointsSame(points1, points2) { + if (points1.length !== points2.length) { + return; + } + for (var i = 0; i < points1.length; i++) { + var p1 = points1[i]; + var p2 = points2[i]; + if (p1[0] !== p2[0] || p1[1] !== p2[1]) { + return; + } + } + return true; + } + + function getSmooth(smooth) { + return typeof (smooth) === 'number' ? smooth : (smooth ? 0.3 : 0); + } + + function getAxisExtentWithGap(axis) { + var extent = axis.getGlobalExtent(); + if (axis.onBand) { + // Remove extra 1px to avoid line miter in clipped edge + var halfBandWidth = axis.getBandWidth() / 2 - 1; + var dir = extent[1] > extent[0] ? 1 : -1; + extent[0] += dir * halfBandWidth; + extent[1] -= dir * halfBandWidth; + } + return extent; + } + + function sign(val) { + return val >= 0 ? 1 : -1; + } + /** + * @param {module:echarts/coord/cartesian/Cartesian2D|module:echarts/coord/polar/Polar} coordSys + * @param {module:echarts/data/List} data + * @param {Array.>} points + * @private + */ + function getStackedOnPoints(coordSys, data) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = baseAxis.onZero + ? 0 : valueAxis.scale.getExtent()[0]; + + var valueDim = valueAxis.dim; + + var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0; + + return data.mapArray([valueDim], function (val, idx) { + var stackedOnSameSign; + var stackedOn = data.stackedOn; + // Find first stacked value with same sign + while (stackedOn && + sign(stackedOn.get(valueDim, idx)) === sign(val) + ) { + stackedOnSameSign = stackedOn; + break; + } + var stackedData = []; + stackedData[baseDataOffset] = data.get(baseAxis.dim, idx); + stackedData[1 - baseDataOffset] = stackedOnSameSign + ? stackedOnSameSign.get(valueDim, idx, true) : valueStart; + + return coordSys.dataToPoint(stackedData); + }, true); + } + + function queryDataIndex(data, payload) { + if (payload.dataIndex != null) { + return payload.dataIndex; + } + else if (payload.name != null) { + return data.indexOfName(payload.name); + } + } + + function createGridClipShape(cartesian, hasAnimation, seriesModel) { + var xExtent = getAxisExtentWithGap(cartesian.getAxis('x')); + var yExtent = getAxisExtentWithGap(cartesian.getAxis('y')); + var isHorizontal = cartesian.getBaseAxis().isHorizontal(); + + var x = Math.min(xExtent[0], xExtent[1]); + var y = Math.min(yExtent[0], yExtent[1]); + var width = Math.max(xExtent[0], xExtent[1]) - x; + var height = Math.max(yExtent[0], yExtent[1]) - y; + var lineWidth = seriesModel.get('lineStyle.normal.width') || 2; + // Expand clip shape to avoid clipping when line value exceeds axis + var expandSize = seriesModel.get('clipOverflow') ? lineWidth / 2 : Math.max(width, height); + if (isHorizontal) { + y -= expandSize; + height += expandSize * 2; + } + else { + x -= expandSize; + width += expandSize * 2; + } + + var clipPath = new graphic.Rect({ + shape: { + x: x, + y: y, + width: width, + height: height + } + }); + + if (hasAnimation) { + clipPath.shape[isHorizontal ? 'width' : 'height'] = 0; + graphic.initProps(clipPath, { + shape: { + width: width, + height: height + } + }, seriesModel); + } + + return clipPath; + } + + function createPolarClipShape(polar, hasAnimation, seriesModel) { + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + + var radiusExtent = radiusAxis.getExtent(); + var angleExtent = angleAxis.getExtent(); + + var RADIAN = Math.PI / 180; + + var clipPath = new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: radiusExtent[0], + r: radiusExtent[1], + startAngle: -angleExtent[0] * RADIAN, + endAngle: -angleExtent[1] * RADIAN, + clockwise: angleAxis.inverse + } + }); + + if (hasAnimation) { + clipPath.shape.endAngle = -angleExtent[0] * RADIAN; + graphic.initProps(clipPath, { + shape: { + endAngle: -angleExtent[1] * RADIAN + } + }, seriesModel); + } + + return clipPath; + } + + function createClipShape(coordSys, hasAnimation, seriesModel) { + return coordSys.type === 'polar' + ? createPolarClipShape(coordSys, hasAnimation, seriesModel) + : createGridClipShape(coordSys, hasAnimation, seriesModel); + } + + function turnPointsIntoStep(points, coordSys, stepTurnAt) { + var baseAxis = coordSys.getBaseAxis(); + var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; + + var stepPoints = []; + for (var i = 0; i < points.length - 1; i++) { + var nextPt = points[i + 1]; + var pt = points[i]; + stepPoints.push(pt); + + var stepPt = []; + switch (stepTurnAt) { + case 'end': + stepPt[baseIndex] = nextPt[baseIndex]; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + // default is start + stepPoints.push(stepPt); + break; + case 'middle': + // default is start + var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2; + var stepPt2 = []; + stepPt[baseIndex] = stepPt2[baseIndex] = middle; + stepPt[1 - baseIndex] = pt[1 - baseIndex]; + stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]; + stepPoints.push(stepPt); + stepPoints.push(stepPt2); + break; + default: + stepPt[baseIndex] = pt[baseIndex]; + stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; + // default is start + stepPoints.push(stepPt); + } + } + // Last points + points[i] && stepPoints.push(points[i]); + return stepPoints; + } + + function clamp(number, extent) { + return Math.max(Math.min(number, extent[1]), extent[0]); + } + + function getVisualGradient(data, coordSys) { + var visualMetaList = data.getVisual('visualMeta'); + if (!visualMetaList || !visualMetaList.length) { + return; + } + + var visualMeta; + for (var i = visualMetaList.length - 1; i >= 0; i--) { + // Can only be x or y + if (visualMetaList[i].dimension < 2) { + visualMeta = visualMetaList[i]; + break; + } + } + if (!visualMeta || coordSys.type !== 'cartesian2d') { + if (true) { + console.warn('Visual map on line style only support x or y dimension.'); + } + return; + } + var dimension = visualMeta.dimension; + var dimName = data.dimensions[dimension]; + var dataExtent = data.getDataExtent(dimName); + + var stops = visualMeta.stops; + + var colorStops = []; + if (stops[0].interval) { + stops.sort(function (a, b) { + return a.interval[0] - b.interval[0]; + }); + } + + var firstStop = stops[0]; + var lastStop = stops[stops.length - 1]; + // Interval can be infinity in piecewise case + var min = firstStop.interval ? clamp(firstStop.interval[0], dataExtent) : firstStop.value; + var max = lastStop.interval ? clamp(lastStop.interval[1], dataExtent) : lastStop.value; + var stopsSpan = max - min; + + // In the piecewise case data out of visual range + // ----dataMin----dataMax-----visualMin----visualMax + if (stopsSpan === 0) { + return data.getItemVisual(0, 'color'); + } + for (var i = 0; i < stops.length; i++) { + // Piecewise + if (stops[i].interval) { + if (stops[i].interval[1] === stops[i].interval[0]) { + continue; + } + colorStops.push({ + // Make sure offset is between 0 and 1 + offset: (clamp(stops[i].interval[0], dataExtent) - min) / stopsSpan, + color: stops[i].color + }, { + offset: (clamp(stops[i].interval[1], dataExtent) - min) / stopsSpan, + color: stops[i].color + }); + } + // Continous + else { + // if (i > 0 && stops[i].value === stops[i - 1].value) { + // continue; + // } + colorStops.push({ + offset: (stops[i].value - min) / stopsSpan, + color: stops[i].color + }); + } + } + var gradient = new graphic.LinearGradient( + 0, 0, 0, 0, colorStops, true + ); + var axis = coordSys.getAxis(dimName); + + var start = Math.round(axis.toGlobalCoord(axis.dataToCoord(min))); + var end = Math.round(axis.toGlobalCoord(axis.dataToCoord(max))); + // zrUtil.each(colorStops, function (colorStop) { + // // Make sure each offset has rounded px to avoid not sharp edge + // colorStop.offset = (Math.round(colorStop.offset * (end - start) + start) - start) / (end - start); + // }); + + gradient[dimName] = start; + gradient[dimName + '2'] = end; + + return gradient; + } + + module.exports = ChartView.extend({ + + type: 'line', + + init: function () { + var lineGroup = new graphic.Group(); + + var symbolDraw = new SymbolDraw(); + this.group.add(symbolDraw.group); + + this._symbolDraw = symbolDraw; + this._lineGroup = lineGroup; + }, + + render: function (seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var group = this.group; + var data = seriesModel.getData(); + var lineStyleModel = seriesModel.getModel('lineStyle.normal'); + var areaStyleModel = seriesModel.getModel('areaStyle.normal'); + + var points = data.mapArray(data.getItemLayout, true); + + var isCoordSysPolar = coordSys.type === 'polar'; + var prevCoordSys = this._coordSys; + + var symbolDraw = this._symbolDraw; + var polyline = this._polyline; + var polygon = this._polygon; + + var lineGroup = this._lineGroup; + + var hasAnimation = seriesModel.get('animation'); + + var isAreaChart = !areaStyleModel.isEmpty(); + var stackedOnPoints = getStackedOnPoints(coordSys, data); + + var showSymbol = seriesModel.get('showSymbol'); + + var isSymbolIgnore = showSymbol && !isCoordSysPolar && !seriesModel.get('showAllSymbol') + && this._getSymbolIgnoreFunc(data, coordSys); + + // Remove temporary symbols + var oldData = this._data; + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + + // Remove previous created symbols if showSymbol changed to false + if (!showSymbol) { + symbolDraw.remove(); + } + + group.add(lineGroup); + + // FIXME step not support polar + var step = !isCoordSysPolar && seriesModel.get('step'); + // Initialization animation or coordinate system changed + if ( + !(polyline && prevCoordSys.type === coordSys.type && step === this._step) + ) { + showSymbol && symbolDraw.updateData(data, isSymbolIgnore); + + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step); + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); + } + + polyline = this._newPolyline(points, coordSys, hasAnimation); + if (isAreaChart) { + polygon = this._newPolygon( + points, stackedOnPoints, + coordSys, hasAnimation + ); + } + lineGroup.setClipPath(createClipShape(coordSys, true, seriesModel)); + } + else { + if (isAreaChart && !polygon) { + // If areaStyle is added + polygon = this._newPolygon( + points, stackedOnPoints, + coordSys, hasAnimation + ); + } + else if (polygon && !isAreaChart) { + // If areaStyle is removed + lineGroup.remove(polygon); + polygon = this._polygon = null; + } + + // Update clipPath + lineGroup.setClipPath(createClipShape(coordSys, false, seriesModel)); + + // Always update, or it is wrong in the case turning on legend + // because points are not changed + showSymbol && symbolDraw.updateData(data, isSymbolIgnore); + + // Stop symbol animation and sync with line points + // FIXME performance? + data.eachItemGraphicEl(function (el) { + el.stopAnimation(true); + }); + + // In the case data zoom triggerred refreshing frequently + // Data may not change if line has a category axis. So it should animate nothing + if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) + || !isPointsSame(this._points, points) + ) { + if (hasAnimation) { + this._updateAnimation( + data, stackedOnPoints, coordSys, api, step + ); + } + else { + // Not do it in update with animation + if (step) { + // TODO If stacked series is not step + points = turnPointsIntoStep(points, coordSys, step); + stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step); + } + + polyline.setShape({ + points: points + }); + polygon && polygon.setShape({ + points: points, + stackedOnPoints: stackedOnPoints + }); + } + } + } + + var visualColor = getVisualGradient(data, coordSys) || data.getVisual('color'); + polyline.useStyle(zrUtil.defaults( + // Use color in lineStyle first + lineStyleModel.getLineStyle(), + { + fill: 'none', + stroke: visualColor, + lineJoin: 'bevel' + } + )); + + var smooth = seriesModel.get('smooth'); + smooth = getSmooth(seriesModel.get('smooth')); + polyline.setShape({ + smooth: smooth, + smoothMonotone: seriesModel.get('smoothMonotone'), + connectNulls: seriesModel.get('connectNulls') + }); + + if (polygon) { + var stackedOn = data.stackedOn; + var stackedOnSmooth = 0; + + polygon.useStyle(zrUtil.defaults( + areaStyleModel.getAreaStyle(), + { + fill: visualColor, + opacity: 0.7, + lineJoin: 'bevel' + } + )); + + if (stackedOn) { + var stackedOnSeries = stackedOn.hostModel; + stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth')); + } + + polygon.setShape({ + smooth: smooth, + stackedOnSmooth: stackedOnSmooth, + smoothMonotone: seriesModel.get('smoothMonotone'), + connectNulls: seriesModel.get('connectNulls') + }); + } + + this._data = data; + // Save the coordinate system for transition animation when data changed + this._coordSys = coordSys; + this._stackedOnPoints = stackedOnPoints; + this._points = points; + this._step = step; + }, + + highlight: function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + + if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) { + var symbol = data.getItemGraphicEl(dataIndex); + if (!symbol) { + // Create a temporary symbol if it is not exists + var pt = data.getItemLayout(dataIndex); + symbol = new Symbol(data, dataIndex); + symbol.position = pt; + symbol.setZ( + seriesModel.get('zlevel'), + seriesModel.get('z') + ); + symbol.ignore = isNaN(pt[0]) || isNaN(pt[1]); + symbol.__temp = true; + data.setItemGraphicEl(dataIndex, symbol); + + // Stop scale animation + symbol.stopSymbolAnimation(true); + + this.group.add(symbol); + } + symbol.highlight(); + } + else { + // Highlight whole series + ChartView.prototype.highlight.call( + this, seriesModel, ecModel, api, payload + ); + } + }, + + downplay: function (seriesModel, ecModel, api, payload) { + var data = seriesModel.getData(); + var dataIndex = queryDataIndex(data, payload); + if (dataIndex != null && dataIndex >= 0) { + var symbol = data.getItemGraphicEl(dataIndex); + if (symbol) { + if (symbol.__temp) { + data.setItemGraphicEl(dataIndex, null); + this.group.remove(symbol); + } + else { + symbol.downplay(); + } + } + } + else { + // Downplay whole series + ChartView.prototype.downplay.call( + this, seriesModel, ecModel, api, payload + ); + } + }, + + /** + * @param {module:zrender/container/Group} group + * @param {Array.>} points + * @private + */ + _newPolyline: function (points) { + var polyline = this._polyline; + // Remove previous created polyline + if (polyline) { + this._lineGroup.remove(polyline); + } + + polyline = new polyHelper.Polyline({ + shape: { + points: points + }, + silent: true, + z2: 10 + }); + + this._lineGroup.add(polyline); + + this._polyline = polyline; + + return polyline; + }, + + /** + * @param {module:zrender/container/Group} group + * @param {Array.>} stackedOnPoints + * @param {Array.>} points + * @private + */ + _newPolygon: function (points, stackedOnPoints) { + var polygon = this._polygon; + // Remove previous created polygon + if (polygon) { + this._lineGroup.remove(polygon); + } + + polygon = new polyHelper.Polygon({ + shape: { + points: points, + stackedOnPoints: stackedOnPoints + }, + silent: true + }); + + this._lineGroup.add(polygon); + + this._polygon = polygon; + return polygon; + }, + /** + * @private + */ + _getSymbolIgnoreFunc: function (data, coordSys) { + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + // `getLabelInterval` is provided by echarts/component/axis + if (categoryAxis && categoryAxis.isLabelIgnored) { + return zrUtil.bind(categoryAxis.isLabelIgnored, categoryAxis); + } + }, + + /** + * @private + */ + // FIXME Two value axis + _updateAnimation: function (data, stackedOnPoints, coordSys, api, step) { + var polyline = this._polyline; + var polygon = this._polygon; + var seriesModel = data.hostModel; + + var diff = lineAnimationDiff( + this._data, data, + this._stackedOnPoints, stackedOnPoints, + this._coordSys, coordSys + ); + + var current = diff.current; + var stackedOnCurrent = diff.stackedOnCurrent; + var next = diff.next; + var stackedOnNext = diff.stackedOnNext; + if (step) { + // TODO If stacked series is not step + current = turnPointsIntoStep(diff.current, coordSys, step); + stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step); + next = turnPointsIntoStep(diff.next, coordSys, step); + stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step); + } + polyline.shape.__points = diff.current; + polyline.shape.points = current; + + graphic.updateProps(polyline, { + shape: { + points: next + } + }, seriesModel); + + if (polygon) { + polygon.setShape({ + points: current, + stackedOnPoints: stackedOnCurrent + }); + graphic.updateProps(polygon, { + shape: { + points: next, + stackedOnPoints: stackedOnNext, + __points: diff.next + } + }, seriesModel); + } + + var updatedDataInfo = []; + var diffStatus = diff.status; + + for (var i = 0; i < diffStatus.length; i++) { + var cmd = diffStatus[i].cmd; + if (cmd === '=') { + var el = data.getItemGraphicEl(diffStatus[i].idx1); + if (el) { + updatedDataInfo.push({ + el: el, + ptIdx: i // Index of points + }); + } + } + } + + if (polyline.animators && polyline.animators.length) { + polyline.animators[0].during(function () { + for (var i = 0; i < updatedDataInfo.length; i++) { + var el = updatedDataInfo[i].el; + el.attr('position', polyline.shape.__points[updatedDataInfo[i].ptIdx]); + } + }); + } + }, + + remove: function (ecModel) { + var group = this.group; + var oldData = this._data; + this._lineGroup.removeAll(); + this._symbolDraw.remove(true); + // Remove temporary created elements when highlighting + oldData && oldData.eachItemGraphicEl(function (el, idx) { + if (el.__temp) { + group.remove(el); + oldData.setItemGraphicEl(idx, null); + } + }); + + this._polyline = + this._polygon = + this._coordSys = + this._points = + this._stackedOnPoints = + this._data = null; + } + }); + + +/***/ }, +/* 104 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/SymbolDraw + */ + + + var graphic = __webpack_require__(43); + var Symbol = __webpack_require__(105); + + /** + * @constructor + * @alias module:echarts/chart/helper/SymbolDraw + * @param {module:zrender/graphic/Group} [symbolCtor] + */ + function SymbolDraw(symbolCtor) { + this.group = new graphic.Group(); + + this._symbolCtor = symbolCtor || Symbol; + } + + var symbolDrawProto = SymbolDraw.prototype; + + function symbolNeedsDraw(data, idx, isIgnore) { + var point = data.getItemLayout(idx); + // Is an object + // if (point && point.hasOwnProperty('point')) { + // point = point.point; + // } + return point && !isNaN(point[0]) && !isNaN(point[1]) && !(isIgnore && isIgnore(idx)) + && data.getItemVisual(idx, 'symbol') !== 'none'; + } + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + * @param {Array.} [isIgnore] + */ + symbolDrawProto.updateData = function (data, isIgnore) { + var group = this.group; + var seriesModel = data.hostModel; + var oldData = this._data; + + var SymbolCtor = this._symbolCtor; + + var seriesScope = { + itemStyle: seriesModel.getModel('itemStyle.normal').getItemStyle(['color']), + hoverItemStyle: seriesModel.getModel('itemStyle.emphasis').getItemStyle(), + symbolRotate: seriesModel.get('symbolRotate'), + symbolOffset: seriesModel.get('symbolOffset'), + hoverAnimation: seriesModel.get('hoverAnimation'), + + labelModel: seriesModel.getModel('label.normal'), + hoverLabelModel: seriesModel.getModel('label.emphasis') + }; + + data.diff(oldData) + .add(function (newIdx) { + var point = data.getItemLayout(newIdx); + if (symbolNeedsDraw(data, newIdx, isIgnore)) { + var symbolEl = new SymbolCtor(data, newIdx, seriesScope); + symbolEl.attr('position', point); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }) + .update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + var point = data.getItemLayout(newIdx); + if (!symbolNeedsDraw(data, newIdx, isIgnore)) { + group.remove(symbolEl); + return; + } + if (!symbolEl) { + symbolEl = new SymbolCtor(data, newIdx); + symbolEl.attr('position', point); + } + else { + symbolEl.updateData(data, newIdx, seriesScope); + graphic.updateProps(symbolEl, { + position: point + }, seriesModel); + } + + // Add back + group.add(symbolEl); + + data.setItemGraphicEl(newIdx, symbolEl); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && el.fadeOut(function () { + group.remove(el); + }); + }) + .execute(); + + this._data = data; + }; + + symbolDrawProto.updateLayout = function () { + var data = this._data; + if (data) { + // Not use animation + data.eachItemGraphicEl(function (el, idx) { + var point = data.getItemLayout(idx); + el.attr('position', point); + }); + } + }; + + symbolDrawProto.remove = function (enableAnimation) { + var group = this.group; + var data = this._data; + if (data) { + if (enableAnimation) { + data.eachItemGraphicEl(function (el) { + el.fadeOut(function () { + group.remove(el); + }); + }); + } + else { + group.removeAll(); + } + } + }; + + module.exports = SymbolDraw; + + +/***/ }, +/* 105 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Symbol + */ + + + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + + function normalizeSymbolSize(symbolSize) { + if (!(symbolSize instanceof Array)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + + /** + * @constructor + * @alias {module:echarts/chart/helper/Symbol} + * @param {module:echarts/data/List} data + * @param {number} idx + * @extends {module:zrender/graphic/Group} + */ + function Symbol(data, idx, seriesScope) { + graphic.Group.call(this); + + this.updateData(data, idx, seriesScope); + } + + var symbolProto = Symbol.prototype; + + function driftSymbol(dx, dy) { + this.parent.drift(dx, dy); + } + + symbolProto._createSymbol = function (symbolType, data, idx) { + // Remove paths created before + this.removeAll(); + + var seriesModel = data.hostModel; + var color = data.getItemVisual(idx, 'color'); + + var symbolPath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + + symbolPath.attr({ + z2: 100, + culling: true, + scale: [0, 0] + }); + // Rewrite drift method + symbolPath.drift = driftSymbol; + + var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + + graphic.initProps(symbolPath, { + scale: size + }, seriesModel, idx); + + this._symbolType = symbolType; + + this.add(symbolPath); + }; + + /** + * Stop animation + * @param {boolean} toLastFrame + */ + symbolProto.stopSymbolAnimation = function (toLastFrame) { + this.childAt(0).stopAnimation(toLastFrame); + }; + + /** + * Get symbol path element + */ + symbolProto.getSymbolPath = function () { + return this.childAt(0); + }; + + /** + * Get scale(aka, current symbol size). + * Including the change caused by animation + */ + symbolProto.getScale = function () { + return this.childAt(0).scale; + }; + + /** + * Highlight symbol + */ + symbolProto.highlight = function () { + this.childAt(0).trigger('emphasis'); + }; + + /** + * Downplay symbol + */ + symbolProto.downplay = function () { + this.childAt(0).trigger('normal'); + }; + + /** + * @param {number} zlevel + * @param {number} z + */ + symbolProto.setZ = function (zlevel, z) { + var symbolPath = this.childAt(0); + symbolPath.zlevel = zlevel; + symbolPath.z = z; + }; + + symbolProto.setDraggable = function (draggable) { + var symbolPath = this.childAt(0); + symbolPath.draggable = draggable; + symbolPath.cursor = draggable ? 'move' : 'pointer'; + }; + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + symbolProto.updateData = function (data, idx, seriesScope) { + this.silent = false; + + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var seriesModel = data.hostModel; + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + if (symbolType !== this._symbolType) { + this._createSymbol(symbolType, data, idx); + } + else { + var symbolPath = this.childAt(0); + graphic.updateProps(symbolPath, { + scale: symbolSize + }, seriesModel, idx); + } + this._updateCommon(data, idx, symbolSize, seriesScope); + + this._seriesModel = seriesModel; + }; + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + var normalLabelAccessPath = ['label', 'normal']; + var emphasisLabelAccessPath = ['label', 'emphasis']; + + symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) { + var symbolPath = this.childAt(0); + var seriesModel = data.hostModel; + var color = data.getItemVisual(idx, 'color'); + + // Reset style + if (symbolPath.type !== 'image') { + symbolPath.useStyle({ + strokeNoScale: true + }); + } + + seriesScope = seriesScope || null; + + var itemStyle = seriesScope && seriesScope.itemStyle; + var hoverItemStyle = seriesScope && seriesScope.hoverItemStyle; + var symbolRotate = seriesScope && seriesScope.symbolRotate; + var symbolOffset = seriesScope && seriesScope.symbolOffset; + var labelModel = seriesScope && seriesScope.labelModel; + var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; + var hoverAnimation = seriesScope && seriesScope.hoverAnimation; + + if (!seriesScope || data.hasItemOption) { + var itemModel = data.getItemModel(idx); + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + itemStyle = itemModel.getModel(normalStyleAccessPath).getItemStyle(['color']); + hoverItemStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + + symbolRotate = itemModel.getShallow('symbolRotate'); + symbolOffset = itemModel.getShallow('symbolOffset'); + + labelModel = itemModel.getModel(normalLabelAccessPath); + hoverLabelModel = itemModel.getModel(emphasisLabelAccessPath); + hoverAnimation = itemModel.getShallow('hoverAnimation'); + } + else { + hoverItemStyle = zrUtil.extend({}, hoverItemStyle); + } + + var elStyle = symbolPath.style; + + symbolPath.rotation = (symbolRotate || 0) * Math.PI / 180 || 0; + + if (symbolOffset) { + symbolPath.attr('position', [ + numberUtil.parsePercent(symbolOffset[0], symbolSize[0]), + numberUtil.parsePercent(symbolOffset[1], symbolSize[1]) + ]); + } + + // PENDING setColor before setStyle!!! + symbolPath.setColor(color); + + symbolPath.setStyle(itemStyle); + + var opacity = data.getItemVisual(idx, 'opacity'); + if (opacity != null) { + elStyle.opacity = opacity; + } + + // Get last value dim + var dimensions = data.dimensions.slice(); + var valueDim; + var dataType; + while (dimensions.length && ( + valueDim = dimensions.pop(), + dataType = data.getDimensionInfo(valueDim).type, + dataType === 'ordinal' || dataType === 'time' + )) {} // jshint ignore:line + + if (valueDim != null && labelModel.getShallow('show')) { + graphic.setText(elStyle, labelModel, color); + elStyle.text = zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal'), + data.get(valueDim, idx) + ); + } + else { + elStyle.text = ''; + } + + if (valueDim != null && hoverLabelModel.getShallow('show')) { + graphic.setText(hoverItemStyle, hoverLabelModel, color); + hoverItemStyle.text = zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis'), + data.get(valueDim, idx) + ); + } + else { + hoverItemStyle.text = ''; + } + + var size = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + + symbolPath.off('mouseover') + .off('mouseout') + .off('emphasis') + .off('normal'); + + symbolPath.hoverStyle = hoverItemStyle; + + graphic.setHoverStyle(symbolPath); + + if (hoverAnimation && seriesModel.ifEnableAnimation()) { + var onEmphasis = function() { + var ratio = size[1] / size[0]; + this.animateTo({ + scale: [ + Math.max(size[0] * 1.1, size[0] + 3), + Math.max(size[1] * 1.1, size[1] + 3 * ratio) + ] + }, 400, 'elasticOut'); + }; + var onNormal = function() { + this.animateTo({ + scale: size + }, 400, 'elasticOut'); + }; + symbolPath.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } + }; + + symbolProto.fadeOut = function (cb) { + var symbolPath = this.childAt(0); + // Avoid mistaken hover when fading out + this.silent = true; + // Not show text when animating + symbolPath.style.text = ''; + graphic.updateProps(symbolPath, { + scale: [0, 0] + }, this._seriesModel, this.dataIndex, cb); + }; + + zrUtil.inherits(Symbol, graphic.Group); + + module.exports = Symbol; + + +/***/ }, +/* 106 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Symbol factory + + + var graphic = __webpack_require__(43); + var BoundingRect = __webpack_require__(9); + + /** + * Triangle shape + * @inner + */ + var Triangle = graphic.extendShape({ + type: 'triangle', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy + height); + path.lineTo(cx - width, cy + height); + path.closePath(); + } + }); + /** + * Diamond shape + * @inner + */ + var Diamond = graphic.extendShape({ + type: 'diamond', + shape: { + cx: 0, + cy: 0, + width: 0, + height: 0 + }, + buildPath: function (path, shape) { + var cx = shape.cx; + var cy = shape.cy; + var width = shape.width / 2; + var height = shape.height / 2; + path.moveTo(cx, cy - height); + path.lineTo(cx + width, cy); + path.lineTo(cx, cy + height); + path.lineTo(cx - width, cy); + path.closePath(); + } + }); + + /** + * Pin shape + * @inner + */ + var Pin = graphic.extendShape({ + type: 'pin', + shape: { + // x, y on the cusp + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (path, shape) { + var x = shape.x; + var y = shape.y; + var w = shape.width / 5 * 3; + // Height must be larger than width + var h = Math.max(w, shape.height); + var r = w / 2; + + // Dist on y with tangent point and circle center + var dy = r * r / (h - r); + var cy = y - h + r + dy; + var angle = Math.asin(dy / r); + // Dist on x with tangent point and circle center + var dx = Math.cos(angle) * r; + + var tanX = Math.sin(angle); + var tanY = Math.cos(angle); + + path.arc( + x, cy, r, + Math.PI - angle, + Math.PI * 2 + angle + ); + + var cpLen = r * 0.6; + var cpLen2 = r * 0.7; + path.bezierCurveTo( + x + dx - tanX * cpLen, cy + dy + tanY * cpLen, + x, y - cpLen2, + x, y + ); + path.bezierCurveTo( + x, y - cpLen2, + x - dx + tanX * cpLen, cy + dy + tanY * cpLen, + x - dx, cy + dy + ); + path.closePath(); + } + }); + + /** + * Arrow shape + * @inner + */ + var Arrow = graphic.extendShape({ + + type: 'arrow', + + shape: { + x: 0, + y: 0, + width: 0, + height: 0 + }, + + buildPath: function (ctx, shape) { + var height = shape.height; + var width = shape.width; + var x = shape.x; + var y = shape.y; + var dx = width / 3 * 2; + ctx.moveTo(x, y); + ctx.lineTo(x + dx, y + height); + ctx.lineTo(x, y + height / 4 * 3); + ctx.lineTo(x - dx, y + height); + ctx.lineTo(x, y); + ctx.closePath(); + } + }); + + /** + * Map of path contructors + * @type {Object.} + */ + var symbolCtors = { + line: graphic.Line, + + rect: graphic.Rect, + + roundRect: graphic.Rect, + + square: graphic.Rect, + + circle: graphic.Circle, + + diamond: Diamond, + + pin: Pin, + + arrow: Arrow, + + triangle: Triangle + }; + + var symbolShapeMakers = { + + line: function (x, y, w, h, shape) { + // FIXME + shape.x1 = x; + shape.y1 = y + h / 2; + shape.x2 = x + w; + shape.y2 = y + h / 2; + }, + + rect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + }, + + roundRect: function (x, y, w, h, shape) { + shape.x = x; + shape.y = y; + shape.width = w; + shape.height = h; + shape.r = Math.min(w, h) / 4; + }, + + square: function (x, y, w, h, shape) { + var size = Math.min(w, h); + shape.x = x; + shape.y = y; + shape.width = size; + shape.height = size; + }, + + circle: function (x, y, w, h, shape) { + // Put circle in the center of square + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.r = Math.min(w, h) / 2; + }, + + diamond: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + }, + + pin: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + + arrow: function (x, y, w, h, shape) { + shape.x = x + w / 2; + shape.y = y + h / 2; + shape.width = w; + shape.height = h; + }, + + triangle: function (x, y, w, h, shape) { + shape.cx = x + w / 2; + shape.cy = y + h / 2; + shape.width = w; + shape.height = h; + } + }; + + var symbolBuildProxies = {}; + for (var name in symbolCtors) { + symbolBuildProxies[name] = new symbolCtors[name](); + } + + var Symbol = graphic.extendShape({ + + type: 'symbol', + + shape: { + symbolType: '', + x: 0, + y: 0, + width: 0, + height: 0 + }, + + beforeBrush: function () { + var style = this.style; + var shape = this.shape; + // FIXME + if (shape.symbolType === 'pin' && style.textPosition === 'inside') { + style.textPosition = ['50%', '40%']; + style.textAlign = 'center'; + style.textVerticalAlign = 'middle'; + } + }, + + buildPath: function (ctx, shape, inBundle) { + var symbolType = shape.symbolType; + var proxySymbol = symbolBuildProxies[symbolType]; + if (shape.symbolType !== 'none') { + if (!proxySymbol) { + // Default rect + symbolType = 'rect'; + proxySymbol = symbolBuildProxies[symbolType]; + } + symbolShapeMakers[symbolType]( + shape.x, shape.y, shape.width, shape.height, proxySymbol.shape + ); + proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle); + } + } + }); + + // Provide setColor helper method to avoid determine if set the fill or stroke outside + var symbolPathSetColor = function (color) { + if (this.type !== 'image') { + var symbolStyle = this.style; + var symbolShape = this.shape; + if (symbolShape && symbolShape.symbolType === 'line') { + symbolStyle.stroke = color; + } + else if (this.__isEmptyBrush) { + symbolStyle.stroke = color; + symbolStyle.fill = '#fff'; + } + else { + // FIXME 判断图形默认是填充还是描边,使用 onlyStroke ? + symbolStyle.fill && (symbolStyle.fill = color); + symbolStyle.stroke && (symbolStyle.stroke = color); + } + this.dirty(false); + } + }; + + var symbolUtil = { + /** + * Create a symbol element with given symbol configuration: shape, x, y, width, height, color + * @param {string} symbolType + * @param {number} x + * @param {number} y + * @param {number} w + * @param {number} h + * @param {string} color + */ + createSymbol: function (symbolType, x, y, w, h, color) { + var isEmpty = symbolType.indexOf('empty') === 0; + if (isEmpty) { + symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6); + } + var symbolPath; + + if (symbolType.indexOf('image://') === 0) { + symbolPath = new graphic.Image({ + style: { + image: symbolType.slice(8), + x: x, + y: y, + width: w, + height: h + } + }); + } + else if (symbolType.indexOf('path://') === 0) { + symbolPath = graphic.makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h)); + } + else { + symbolPath = new Symbol({ + shape: { + symbolType: symbolType, + x: x, + y: y, + width: w, + height: h + } + }); + } + + symbolPath.__isEmptyBrush = isEmpty; + + symbolPath.setColor = symbolPathSetColor; + + symbolPath.setColor(color); + + return symbolPath; + } + }; + + module.exports = symbolUtil; + + +/***/ }, +/* 107 */ +/***/ function(module, exports) { + + + + // var arrayDiff = require('zrender/lib/core/arrayDiff'); + // 'zrender/core/arrayDiff' has been used before, but it did + // not do well in performance when roam with fixed dataZoom window. + + function sign(val) { + return val >= 0 ? 1 : -1; + } + + function getStackedOnPoint(coordSys, data, idx) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var valueStart = baseAxis.onZero + ? 0 : valueAxis.scale.getExtent()[0]; + + var valueDim = valueAxis.dim; + var baseDataOffset = valueDim === 'x' || valueDim === 'radius' ? 1 : 0; + + var stackedOnSameSign; + var stackedOn = data.stackedOn; + var val = data.get(valueDim, idx); + // Find first stacked value with same sign + while (stackedOn && + sign(stackedOn.get(valueDim, idx)) === sign(val) + ) { + stackedOnSameSign = stackedOn; + break; + } + var stackedData = []; + stackedData[baseDataOffset] = data.get(baseAxis.dim, idx); + stackedData[1 - baseDataOffset] = stackedOnSameSign + ? stackedOnSameSign.get(valueDim, idx, true) : valueStart; + + return coordSys.dataToPoint(stackedData); + } + + // function convertToIntId(newIdList, oldIdList) { + // // Generate int id instead of string id. + // // Compare string maybe slow in score function of arrDiff + + // // Assume id in idList are all unique + // var idIndicesMap = {}; + // var idx = 0; + // for (var i = 0; i < newIdList.length; i++) { + // idIndicesMap[newIdList[i]] = idx; + // newIdList[i] = idx++; + // } + // for (var i = 0; i < oldIdList.length; i++) { + // var oldId = oldIdList[i]; + // // Same with newIdList + // if (idIndicesMap[oldId]) { + // oldIdList[i] = idIndicesMap[oldId]; + // } + // else { + // oldIdList[i] = idx++; + // } + // } + // } + + function diffData(oldData, newData) { + var diffResult = []; + + newData.diff(oldData) + .add(function (idx) { + diffResult.push({cmd: '+', idx: idx}); + }) + .update(function (newIdx, oldIdx) { + diffResult.push({cmd: '=', idx: oldIdx, idx1: newIdx}); + }) + .remove(function (idx) { + diffResult.push({cmd: '-', idx: idx}); + }) + .execute(); + + return diffResult; + } + + module.exports = function ( + oldData, newData, + oldStackedOnPoints, newStackedOnPoints, + oldCoordSys, newCoordSys + ) { + var diff = diffData(oldData, newData); + + // var newIdList = newData.mapArray(newData.getId); + // var oldIdList = oldData.mapArray(oldData.getId); + + // convertToIntId(newIdList, oldIdList); + + // // FIXME One data ? + // diff = arrayDiff(oldIdList, newIdList); + + var currPoints = []; + var nextPoints = []; + // Points for stacking base line + var currStackedPoints = []; + var nextStackedPoints = []; + + var status = []; + var sortedIndices = []; + var rawIndices = []; + var dims = newCoordSys.dimensions; + for (var i = 0; i < diff.length; i++) { + var diffItem = diff[i]; + var pointAdded = true; + + // FIXME, animation is not so perfect when dataZoom window moves fast + // Which is in case remvoing or add more than one data in the tail or head + switch (diffItem.cmd) { + case '=': + var currentPt = oldData.getItemLayout(diffItem.idx); + var nextPt = newData.getItemLayout(diffItem.idx1); + // If previous data is NaN, use next point directly + if (isNaN(currentPt[0]) || isNaN(currentPt[1])) { + currentPt = nextPt.slice(); + } + currPoints.push(currentPt); + nextPoints.push(nextPt); + + currStackedPoints.push(oldStackedOnPoints[diffItem.idx]); + nextStackedPoints.push(newStackedOnPoints[diffItem.idx1]); + + rawIndices.push(newData.getRawIndex(diffItem.idx1)); + break; + case '+': + var idx = diffItem.idx; + currPoints.push( + oldCoordSys.dataToPoint([ + newData.get(dims[0], idx, true), newData.get(dims[1], idx, true) + ]) + ); + + nextPoints.push(newData.getItemLayout(idx).slice()); + + currStackedPoints.push( + getStackedOnPoint(oldCoordSys, newData, idx) + ); + nextStackedPoints.push(newStackedOnPoints[idx]); + + rawIndices.push(newData.getRawIndex(idx)); + break; + case '-': + var idx = diffItem.idx; + var rawIndex = oldData.getRawIndex(idx); + // Data is replaced. In the case of dynamic data queue + // FIXME FIXME FIXME + if (rawIndex !== idx) { + currPoints.push(oldData.getItemLayout(idx)); + nextPoints.push(newCoordSys.dataToPoint([ + oldData.get(dims[0], idx, true), oldData.get(dims[1], idx, true) + ])); + + currStackedPoints.push(oldStackedOnPoints[idx]); + nextStackedPoints.push( + getStackedOnPoint( + newCoordSys, oldData, idx + ) + ); + + rawIndices.push(rawIndex); + } + else { + pointAdded = false; + } + } + + // Original indices + if (pointAdded) { + status.push(diffItem); + sortedIndices.push(sortedIndices.length); + } + } + + // Diff result may be crossed if all items are changed + // Sort by data index + sortedIndices.sort(function (a, b) { + return rawIndices[a] - rawIndices[b]; + }); + + var sortedCurrPoints = []; + var sortedNextPoints = []; + + var sortedCurrStackedPoints = []; + var sortedNextStackedPoints = []; + + var sortedStatus = []; + for (var i = 0; i < sortedIndices.length; i++) { + var idx = sortedIndices[i]; + sortedCurrPoints[i] = currPoints[idx]; + sortedNextPoints[i] = nextPoints[idx]; + + sortedCurrStackedPoints[i] = currStackedPoints[idx]; + sortedNextStackedPoints[i] = nextStackedPoints[idx]; + + sortedStatus[i] = status[idx]; + } + + return { + current: sortedCurrPoints, + next: sortedNextPoints, + + stackedOnCurrent: sortedCurrStackedPoints, + stackedOnNext: sortedNextStackedPoints, + + status: sortedStatus + }; + }; + + +/***/ }, +/* 108 */ +/***/ function(module, exports, __webpack_require__) { + + // Poly path support NaN point + + + var Path = __webpack_require__(45); + var vec2 = __webpack_require__(10); + + var vec2Min = vec2.min; + var vec2Max = vec2.max; + + var scaleAndAdd = vec2.scaleAndAdd; + var v2Copy = vec2.copy; + + // Temporary variable + var v = []; + var cp0 = []; + var cp1 = []; + + function isPointNull(p) { + return isNaN(p[0]) || isNaN(p[1]); + } + + function drawSegment( + ctx, points, start, segLen, allLen, + dir, smoothMin, smoothMax, smooth, smoothMonotone, connectNulls + ) { + var prevIdx = 0; + var idx = start; + for (var k = 0; k < segLen; k++) { + var p = points[idx]; + if (idx >= allLen || idx < 0) { + break; + } + if (isPointNull(p)) { + if (connectNulls) { + idx += dir; + continue; + } + break; + } + + if (idx === start) { + ctx[dir > 0 ? 'moveTo' : 'lineTo'](p[0], p[1]); + v2Copy(cp0, p); + } + else { + if (smooth > 0) { + var nextIdx = idx + dir; + var nextP = points[nextIdx]; + if (connectNulls) { + // Find next point not null + while (nextP && isPointNull(points[nextIdx])) { + nextIdx += dir; + nextP = points[nextIdx]; + } + } + + var ratioNextSeg = 0.5; + var prevP = points[prevIdx]; + var nextP = points[nextIdx]; + // Last point + if (!nextP || isPointNull(nextP)) { + v2Copy(cp1, p); + } + else { + // If next data is null in not connect case + if (isPointNull(nextP) && !connectNulls) { + nextP = p; + } + + vec2.sub(v, nextP, prevP); + + var lenPrevSeg; + var lenNextSeg; + if (smoothMonotone === 'x' || smoothMonotone === 'y') { + var dim = smoothMonotone === 'x' ? 0 : 1; + lenPrevSeg = Math.abs(p[dim] - prevP[dim]); + lenNextSeg = Math.abs(p[dim] - nextP[dim]); + } + else { + lenPrevSeg = vec2.dist(p, prevP); + lenNextSeg = vec2.dist(p, nextP); + } + + // Use ratio of seg length + ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); + + scaleAndAdd(cp1, p, v, -smooth * (1 - ratioNextSeg)); + } + // Smooth constraint + vec2Min(cp0, cp0, smoothMax); + vec2Max(cp0, cp0, smoothMin); + vec2Min(cp1, cp1, smoothMax); + vec2Max(cp1, cp1, smoothMin); + + ctx.bezierCurveTo( + cp0[0], cp0[1], + cp1[0], cp1[1], + p[0], p[1] + ); + // cp0 of next segment + scaleAndAdd(cp0, p, v, smooth * ratioNextSeg); + } + else { + ctx.lineTo(p[0], p[1]); + } + } + + prevIdx = idx; + idx += dir; + } + + return k; + } + + function getBoundingBox(points, smoothConstraint) { + var ptMin = [Infinity, Infinity]; + var ptMax = [-Infinity, -Infinity]; + if (smoothConstraint) { + for (var i = 0; i < points.length; i++) { + var pt = points[i]; + if (pt[0] < ptMin[0]) { ptMin[0] = pt[0]; } + if (pt[1] < ptMin[1]) { ptMin[1] = pt[1]; } + if (pt[0] > ptMax[0]) { ptMax[0] = pt[0]; } + if (pt[1] > ptMax[1]) { ptMax[1] = pt[1]; } + } + } + return { + min: smoothConstraint ? ptMin : ptMax, + max: smoothConstraint ? ptMax : ptMin + }; + } + + module.exports = { + + Polyline: Path.extend({ + + type: 'ec-polyline', + + shape: { + points: [], + + smooth: 0, + + smoothConstraint: true, + + smoothMonotone: null, + + connectNulls: false + }, + + style: { + fill: null, + + stroke: '#000' + }, + + buildPath: function (ctx, shape) { + var points = shape.points; + + var i = 0; + var len = points.length; + + var result = getBoundingBox(points, shape.smoothConstraint); + + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len) { + i += drawSegment( + ctx, points, i, len, len, + 1, result.min, result.max, shape.smooth, + shape.smoothMonotone, shape.connectNulls + ) + 1; + } + } + }), + + Polygon: Path.extend({ + + type: 'ec-polygon', + + shape: { + points: [], + + // Offset between stacked base points and points + stackedOnPoints: [], + + smooth: 0, + + stackedOnSmooth: 0, + + smoothConstraint: true, + + smoothMonotone: null, + + connectNulls: false + }, + + buildPath: function (ctx, shape) { + var points = shape.points; + var stackedOnPoints = shape.stackedOnPoints; + + var i = 0; + var len = points.length; + var smoothMonotone = shape.smoothMonotone; + var bbox = getBoundingBox(points, shape.smoothConstraint); + var stackedOnBBox = getBoundingBox(stackedOnPoints, shape.smoothConstraint); + + if (shape.connectNulls) { + // Must remove first and last null values avoid draw error in polygon + for (; len > 0; len--) { + if (!isPointNull(points[len - 1])) { + break; + } + } + for (; i < len; i++) { + if (!isPointNull(points[i])) { + break; + } + } + } + while (i < len) { + var k = drawSegment( + ctx, points, i, len, len, + 1, bbox.min, bbox.max, shape.smooth, + smoothMonotone, shape.connectNulls + ); + drawSegment( + ctx, stackedOnPoints, i + k - 1, k, len, + -1, stackedOnBBox.min, stackedOnBBox.max, shape.stackedOnSmooth, + smoothMonotone, shape.connectNulls + ); + i += k + 1; + + ctx.closePath(); + } + } + }) + }; + + +/***/ }, +/* 109 */ +/***/ function(module, exports) { + + + + module.exports = function (seriesType, defaultSymbolType, legendSymbol, ecModel, api) { + + // Encoding visual for all series include which is filtered for legend drawing + ecModel.eachRawSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + + var symbolType = seriesModel.get('symbol') || defaultSymbolType; + var symbolSize = seriesModel.get('symbolSize'); + + data.setVisual({ + legendSymbol: legendSymbol || symbolType, + symbol: symbolType, + symbolSize: symbolSize + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + if (typeof symbolSize === 'function') { + data.each(function (idx) { + var rawValue = seriesModel.getRawValue(idx); + // FIXME + var params = seriesModel.getDataParams(idx); + data.setItemVisual(idx, 'symbolSize', symbolSize(rawValue, params)); + }); + } + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var itemSymbolType = itemModel.getShallow('symbol', true); + var itemSymbolSize = itemModel.getShallow('symbolSize', true); + // If has item symbol + if (itemSymbolType != null) { + data.setItemVisual(idx, 'symbol', itemSymbolType); + } + if (itemSymbolSize != null) { + // PENDING Transform symbolSize ? + data.setItemVisual(idx, 'symbolSize', itemSymbolSize); + } + }); + } + }); + }; + + +/***/ }, +/* 110 */ +/***/ function(module, exports) { + + + + module.exports = function (seriesType, ecModel) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + + if (coordSys) { + var dims = coordSys.dimensions; + + if (coordSys.type === 'singleAxis') { + data.each(dims[0], function (x, idx) { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout(idx, isNaN(x) ? [NaN, NaN] : coordSys.dataToPoint(x)); + }); + } + else { + data.each(dims, function (x, y, idx) { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout( + idx, (isNaN(x) || isNaN(y)) ? [NaN, NaN] : coordSys.dataToPoint([x, y]) + ); + }, true); + } + } + }); + }; + + +/***/ }, +/* 111 */ +/***/ function(module, exports) { + + + var samplers = { + average: function (frame) { + var sum = 0; + var count = 0; + for (var i = 0; i < frame.length; i++) { + if (!isNaN(frame[i])) { + sum += frame[i]; + count++; + } + } + // Return NaN if count is 0 + return count === 0 ? NaN : sum / count; + }, + sum: function (frame) { + var sum = 0; + for (var i = 0; i < frame.length; i++) { + // Ignore NaN + sum += frame[i] || 0; + } + return sum; + }, + max: function (frame) { + var max = -Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] > max && (max = frame[i]); + } + return max; + }, + min: function (frame) { + var min = Infinity; + for (var i = 0; i < frame.length; i++) { + frame[i] < min && (min = frame[i]); + } + return min; + }, + // TODO + // Median + nearest: function (frame) { + return frame[0]; + } + }; + + var indexSampler = function (frame, value) { + return Math.round(frame.length / 2); + }; + module.exports = function (seriesType, ecModel, api) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var data = seriesModel.getData(); + var sampling = seriesModel.get('sampling'); + var coordSys = seriesModel.coordinateSystem; + // Only cartesian2d support down sampling + if (coordSys.type === 'cartesian2d' && sampling) { + var baseAxis = coordSys.getBaseAxis(); + var valueAxis = coordSys.getOtherAxis(baseAxis); + var extent = baseAxis.getExtent(); + // Coordinste system has been resized + var size = extent[1] - extent[0]; + var rate = Math.round(data.count() / size); + if (rate > 1) { + var sampler; + if (typeof sampling === 'string') { + sampler = samplers[sampling]; + } + else if (typeof sampling === 'function') { + sampler = sampling; + } + if (sampler) { + data = data.downSample( + valueAxis.dim, 1 / rate, sampler, indexSampler + ); + seriesModel.setData(data); + } + } + } + }, this); + }; + + +/***/ }, +/* 112 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(113); + + __webpack_require__(130); + + // Grid view + echarts.extendComponentView({ + + type: 'grid', + + render: function (gridModel, ecModel) { + this.group.removeAll(); + if (gridModel.get('show')) { + this.group.add(new graphic.Rect({ + shape:gridModel.coordinateSystem.getRect(), + style: zrUtil.defaults({ + fill: gridModel.get('backgroundColor') + }, gridModel.getItemStyle()), + silent: true + })); + } + } + }); + + echarts.registerPreprocessor(function (option) { + // Only create grid when need + if (option.xAxis && option.yAxis && !option.grid) { + option.grid = {}; + } + }); + + +/***/ }, +/* 113 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Grid is a region which contains at most 4 cartesian systems + * + * TODO Default cartesian + */ + var factory = exports; + + var layout = __webpack_require__(21); + var axisHelper = __webpack_require__(114); + + var zrUtil = __webpack_require__(4); + var Cartesian2D = __webpack_require__(120); + var Axis2D = __webpack_require__(122); + + var each = zrUtil.each; + + var ifAxisCrossZero = axisHelper.ifAxisCrossZero; + var niceScaleExtent = axisHelper.niceScaleExtent; + + // 依赖 GridModel, AxisModel 做预处理 + __webpack_require__(125); + + /** + * Check if the axis is used in the specified grid + * @inner + */ + function isAxisUsedInTheGrid(axisModel, gridModel, ecModel) { + return axisModel.findGridModel() === gridModel; + } + + function getLabelUnionRect(axis) { + var axisModel = axis.model; + var labels = axisModel.getFormattedLabels(); + var rect; + var step = 1; + var labelCount = labels.length; + if (labelCount > 40) { + // Simple optimization for large amount of labels + step = Math.ceil(labelCount / 40); + } + for (var i = 0; i < labelCount; i += step) { + if (!axis.isLabelIgnored(i)) { + var singleRect = axisModel.getTextRect(labels[i]); + // FIXME consider label rotate + rect ? rect.union(singleRect) : (rect = singleRect); + } + } + return rect; + } + + function Grid(gridModel, ecModel, api) { + /** + * @type {Object.} + * @private + */ + this._coordsMap = {}; + + /** + * @type {Array.} + * @private + */ + this._coordsList = []; + + /** + * @type {Object.} + * @private + */ + this._axesMap = {}; + + /** + * @type {Array.} + * @private + */ + this._axesList = []; + + this._initCartesian(gridModel, ecModel, api); + + this._model = gridModel; + } + + var gridProto = Grid.prototype; + + gridProto.type = 'grid'; + + gridProto.getRect = function () { + return this._rect; + }; + + gridProto.update = function (ecModel, api) { + + var axesMap = this._axesMap; + + this._updateScale(ecModel, this._model); + + function ifAxisCanNotOnZero(otherAxisDim) { + var axes = axesMap[otherAxisDim]; + for (var idx in axes) { + var axis = axes[idx]; + if (axis && (axis.type === 'category' || !ifAxisCrossZero(axis))) { + return true; + } + } + return false; + } + + each(axesMap.x, function (xAxis) { + niceScaleExtent(xAxis, xAxis.model); + }); + each(axesMap.y, function (yAxis) { + niceScaleExtent(yAxis, yAxis.model); + }); + // Fix configuration + each(axesMap.x, function (xAxis) { + // onZero can not be enabled in these two situations + // 1. When any other axis is a category axis + // 2. When any other axis not across 0 point + if (ifAxisCanNotOnZero('y')) { + xAxis.onZero = false; + } + }); + each(axesMap.y, function (yAxis) { + if (ifAxisCanNotOnZero('x')) { + yAxis.onZero = false; + } + }); + + // Resize again if containLabel is enabled + // FIXME It may cause getting wrong grid size in data processing stage + this.resize(this._model, api); + }; + + /** + * Resize the grid + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {module:echarts/ExtensionAPI} api + */ + gridProto.resize = function (gridModel, api) { + + var gridRect = layout.getLayoutRect( + gridModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }); + + this._rect = gridRect; + + var axesList = this._axesList; + + adjustAxes(); + + // Minus label size + if (gridModel.get('containLabel')) { + each(axesList, function (axis) { + if (!axis.model.get('axisLabel.inside')) { + var labelUnionRect = getLabelUnionRect(axis); + if (labelUnionRect) { + var dim = axis.isHorizontal() ? 'height' : 'width'; + var margin = axis.model.get('axisLabel.margin'); + gridRect[dim] -= labelUnionRect[dim] + margin; + if (axis.position === 'top') { + gridRect.y += labelUnionRect.height + margin; + } + else if (axis.position === 'left') { + gridRect.x += labelUnionRect.width + margin; + } + } + } + }); + + adjustAxes(); + } + + function adjustAxes() { + each(axesList, function (axis) { + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height]; + var idx = axis.inverse ? 1 : 0; + axis.setExtent(extent[idx], extent[1 - idx]); + updateAxisTransfrom(axis, isHorizontal ? gridRect.x : gridRect.y); + }); + } + }; + + /** + * @param {string} axisType + * @param {ndumber} [axisIndex] + */ + gridProto.getAxis = function (axisType, axisIndex) { + var axesMapOnDim = this._axesMap[axisType]; + if (axesMapOnDim != null) { + if (axisIndex == null) { + // Find first axis + for (var name in axesMapOnDim) { + return axesMapOnDim[name]; + } + } + return axesMapOnDim[axisIndex]; + } + }; + + gridProto.getCartesian = function (xAxisIndex, yAxisIndex) { + if (xAxisIndex != null && yAxisIndex != null) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + return this._coordsMap[key]; + } + else { + // When only xAxisIndex or yAxisIndex given, find its first cartesian. + for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) { + if (coordList[i].getAxis('x').index === xAxisIndex + || coordList[i].getAxis('y').index === yAxisIndex + ) { + return coordList[i]; + } + } + } + }; + + /** + * Initialize cartesian coordinate systems + * @private + */ + gridProto._initCartesian = function (gridModel, ecModel, api) { + var axisPositionUsed = { + left: false, + right: false, + top: false, + bottom: false + }; + + var axesMap = { + x: {}, + y: {} + }; + var axesCount = { + x: 0, + y: 0 + }; + + /// Create axis + ecModel.eachComponent('xAxis', createAxisCreator('x'), this); + ecModel.eachComponent('yAxis', createAxisCreator('y'), this); + + if (!axesCount.x || !axesCount.y) { + // Roll back when there no either x or y axis + this._axesMap = {}; + this._axesList = []; + return; + } + + this._axesMap = axesMap; + + /// Create cartesian2d + each(axesMap.x, function (xAxis, xAxisIndex) { + each(axesMap.y, function (yAxis, yAxisIndex) { + var key = 'x' + xAxisIndex + 'y' + yAxisIndex; + var cartesian = new Cartesian2D(key); + + cartesian.grid = this; + + this._coordsMap[key] = cartesian; + this._coordsList.push(cartesian); + + cartesian.addAxis(xAxis); + cartesian.addAxis(yAxis); + }, this); + }, this); + + function createAxisCreator(axisType) { + return function (axisModel, idx) { + if (!isAxisUsedInTheGrid(axisModel, gridModel, ecModel)) { + return; + } + + var axisPosition = axisModel.get('position'); + if (axisType === 'x') { + // Fix position + if (axisPosition !== 'top' && axisPosition !== 'bottom') { + // Default bottom of X + axisPosition = 'bottom'; + if (axisPositionUsed[axisPosition]) { + axisPosition = axisPosition === 'top' ? 'bottom' : 'top'; + } + } + } + else { + // Fix position + if (axisPosition !== 'left' && axisPosition !== 'right') { + // Default left of Y + axisPosition = 'left'; + if (axisPositionUsed[axisPosition]) { + axisPosition = axisPosition === 'left' ? 'right' : 'left'; + } + } + } + axisPositionUsed[axisPosition] = true; + + var axis = new Axis2D( + axisType, axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisPosition + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + + axis.onZero = axisModel.get('axisLine.onZero'); + + // Inject axis into axisModel + axisModel.axis = axis; + + // Inject axisModel into axis + axis.model = axisModel; + + // Inject grid info axis + axis.grid = this; + + // Index of axis, can be used as key + axis.index = idx; + + this._axesList.push(axis); + + axesMap[axisType][idx] = axis; + axesCount[axisType]++; + }; + } + }; + + /** + * Update cartesian properties from series + * @param {module:echarts/model/Option} option + * @private + */ + gridProto._updateScale = function (ecModel, gridModel) { + // Reset scale + zrUtil.each(this._axesList, function (axis) { + axis.scale.setExtent(Infinity, -Infinity); + }); + ecModel.eachSeries(function (seriesModel) { + if (isCartesian2D(seriesModel)) { + var axesModels = findAxesModels(seriesModel, ecModel); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + if (!isAxisUsedInTheGrid(xAxisModel, gridModel, ecModel) + || !isAxisUsedInTheGrid(yAxisModel, gridModel, ecModel) + ) { + return; + } + + var cartesian = this.getCartesian( + xAxisModel.componentIndex, yAxisModel.componentIndex + ); + var data = seriesModel.getData(); + var xAxis = cartesian.getAxis('x'); + var yAxis = cartesian.getAxis('y'); + + if (data.type === 'list') { + unionExtent(data, xAxis, seriesModel); + unionExtent(data, yAxis, seriesModel); + } + } + }, this); + + function unionExtent(data, axis, seriesModel) { + each(seriesModel.coordDimToDataDim(axis.dim), function (dim) { + axis.scale.unionExtent(data.getDataExtent( + dim, axis.scale.type !== 'ordinal' + )); + }); + } + }; + + /** + * @inner + */ + function updateAxisTransfrom(axis, coordBase) { + var axisExtent = axis.getExtent(); + var axisExtentSum = axisExtent[0] + axisExtent[1]; + + // Fast transform + axis.toGlobalCoord = axis.dim === 'x' + ? function (coord) { + return coord + coordBase; + } + : function (coord) { + return axisExtentSum - coord + coordBase; + }; + axis.toLocalCoord = axis.dim === 'x' + ? function (coord) { + return coord - coordBase; + } + : function (coord) { + return axisExtentSum - coord + coordBase; + }; + } + + var axesTypes = ['xAxis', 'yAxis']; + /** + * @inner + */ + function findAxesModels(seriesModel, ecModel) { + return zrUtil.map(axesTypes, function (axisType) { + var axisModel = ecModel.queryComponents({ + mainType: axisType, + index: seriesModel.get(axisType + 'Index'), + id: seriesModel.get(axisType + 'Id') + })[0]; + + if (true) { + if (!axisModel) { + throw new Error(axisType + ' "' + zrUtil.retrieve( + seriesModel.get(axisType + 'Index'), + seriesModel.get(axisType + 'Id'), + 0 + ) + '" not found'); + } + } + return axisModel; + }); + } + + /** + * @inner + */ + function isCartesian2D(seriesModel) { + return seriesModel.get('coordinateSystem') === 'cartesian2d'; + } + + Grid.create = function (ecModel, api) { + var grids = []; + ecModel.eachComponent('grid', function (gridModel, idx) { + var grid = new Grid(gridModel, ecModel, api); + grid.name = 'grid_' + idx; + grid.resize(gridModel, api); + + gridModel.coordinateSystem = grid; + + grids.push(grid); + }); + + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (!isCartesian2D(seriesModel)) { + return; + } + + var axesModels = findAxesModels(seriesModel, ecModel); + var xAxisModel = axesModels[0]; + var yAxisModel = axesModels[1]; + + var gridModel = xAxisModel.findGridModel(); + + if (true) { + if (!gridModel) { + throw new Error( + 'Grid "' + zrUtil.retrieve( + xAxisModel.get('gridIndex'), + xAxisModel.get('gridId'), + 0 + ) + '" not found' + ); + } + if (xAxisModel.findGridModel() !== yAxisModel.findGridModel()) { + throw new Error('xAxis and yAxis must use the same grid'); + } + } + + var grid = gridModel.coordinateSystem; + + seriesModel.coordinateSystem = grid.getCartesian( + xAxisModel.componentIndex, yAxisModel.componentIndex + ); + }); + + return grids; + }; + + // For deciding which dimensions to use when creating list data + Grid.dimensions = Cartesian2D.prototype.dimensions; + + __webpack_require__(26).register('cartesian2d', Grid); + + module.exports = Grid; + + +/***/ }, +/* 114 */ +/***/ function(module, exports, __webpack_require__) { + + + + var OrdinalScale = __webpack_require__(115); + var IntervalScale = __webpack_require__(117); + __webpack_require__(118); + __webpack_require__(119); + var Scale = __webpack_require__(116); + + var numberUtil = __webpack_require__(7); + var zrUtil = __webpack_require__(4); + var textContain = __webpack_require__(8); + var axisHelper = {}; + + /** + * Get axis scale extent before niced. + */ + axisHelper.getScaleExtent = function (axis, model) { + var scale = axis.scale; + var originalExtent = scale.getExtent(); + var span = originalExtent[1] - originalExtent[0]; + if (scale.type === 'ordinal') { + // If series has no data, scale extent may be wrong + if (!isFinite(span)) { + return [0, 0]; + } + else { + return originalExtent; + } + } + var min = model.getMin ? model.getMin() : model.get('min'); + var max = model.getMax ? model.getMax() : model.get('max'); + var crossZero = model.getNeedCrossZero + ? model.getNeedCrossZero() : !model.get('scale'); + var boundaryGap = model.get('boundaryGap'); + if (!zrUtil.isArray(boundaryGap)) { + boundaryGap = [boundaryGap || 0, boundaryGap || 0]; + } + boundaryGap[0] = numberUtil.parsePercent(boundaryGap[0], 1); + boundaryGap[1] = numberUtil.parsePercent(boundaryGap[1], 1); + var fixMin = true; + var fixMax = true; + // Add boundary gap + if (min == null) { + min = originalExtent[0] - boundaryGap[0] * span; + fixMin = false; + } + if (max == null) { + max = originalExtent[1] + boundaryGap[1] * span; + fixMax = false; + } + if (min === 'dataMin') { + min = originalExtent[0]; + } + if (max === 'dataMax') { + max = originalExtent[1]; + } + // Evaluate if axis needs cross zero + if (crossZero) { + // Axis is over zero and min is not set + if (min > 0 && max > 0 && !fixMin) { + min = 0; + } + // Axis is under zero and max is not set + if (min < 0 && max < 0 && !fixMax) { + max = 0; + } + } + return [min, max]; + }; + + axisHelper.niceScaleExtent = function (axis, model) { + var scale = axis.scale; + var extent = axisHelper.getScaleExtent(axis, model); + var fixMin = (model.getMin ? model.getMin() : model.get('min')) != null; + var fixMax = (model.getMax ? model.getMax() : model.get('max')) != null; + var splitNumber = model.get('splitNumber'); + + if (scale.type === 'log') { + scale.base = model.get('logBase'); + } + + scale.setExtent(extent[0], extent[1]); + scale.niceExtent(splitNumber, fixMin, fixMax); + + // Use minInterval to constraint the calculated interval. + // If calculated interval is less than minInterval. increase the interval quantity until + // it is larger than minInterval. + // For example: + // minInterval is 1, calculated interval is 0.2, so increase it to be 1. In this way we can get + // an integer axis. + var minInterval = model.get('minInterval'); + if (isFinite(minInterval) && !fixMin && !fixMax && scale.type === 'interval') { + var interval = scale.getInterval(); + var intervalScale = Math.max(Math.abs(interval), minInterval) / interval; + // while (interval < minInterval) { + // var quantity = numberUtil.quantity(interval); + // interval = quantity * 10; + // scaleQuantity *= 10; + // } + extent = scale.getExtent(); + scale.setExtent(intervalScale * extent[0], extent[1] * intervalScale); + scale.niceExtent(splitNumber); + } + + // If some one specified the min, max. And the default calculated interval + // is not good enough. He can specify the interval. It is often appeared + // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard + // to be 60. + // FIXME + var interval = model.get('interval'); + if (interval != null) { + scale.setInterval && scale.setInterval(interval); + } + }; + + /** + * @param {module:echarts/model/Model} model + * @param {string} [axisType] Default retrieve from model.type + * @return {module:echarts/scale/*} + */ + axisHelper.createScaleByModel = function(model, axisType) { + axisType = axisType || model.get('type'); + if (axisType) { + switch (axisType) { + // Buildin scale + case 'category': + return new OrdinalScale( + model.getCategories(), [Infinity, -Infinity] + ); + case 'value': + return new IntervalScale(); + // Extended scale, like time and log + default: + return (Scale.getClass(axisType) || IntervalScale).create(model); + } + } + }; + + /** + * Check if the axis corss 0 + */ + axisHelper.ifAxisCrossZero = function (axis) { + var dataExtent = axis.scale.getExtent(); + var min = dataExtent[0]; + var max = dataExtent[1]; + return !((min > 0 && max > 0) || (min < 0 && max < 0)); + }; + + /** + * @param {Array.} tickCoords In axis self coordinate. + * @param {Array.} labels + * @param {string} font + * @param {boolean} isAxisHorizontal + * @return {number} + */ + axisHelper.getAxisLabelInterval = function (tickCoords, labels, font, isAxisHorizontal) { + // FIXME + // 不同角的axis和label,不只是horizontal和vertical. + + var textSpaceTakenRect; + var autoLabelInterval = 0; + var accumulatedLabelInterval = 0; + + var step = 1; + if (labels.length > 40) { + // Simple optimization for large amount of labels + step = Math.floor(labels.length / 40); + } + + for (var i = 0; i < tickCoords.length; i += step) { + var tickCoord = tickCoords[i]; + var rect = textContain.getBoundingRect( + labels[i], font, 'center', 'top' + ); + rect[isAxisHorizontal ? 'x' : 'y'] += tickCoord; + // FIXME Magic number 1.5 + rect[isAxisHorizontal ? 'width' : 'height'] *= 1.3; + if (!textSpaceTakenRect) { + textSpaceTakenRect = rect.clone(); + } + // There is no space for current label; + else if (textSpaceTakenRect.intersect(rect)) { + accumulatedLabelInterval++; + autoLabelInterval = Math.max(autoLabelInterval, accumulatedLabelInterval); + } + else { + textSpaceTakenRect.union(rect); + // Reset + accumulatedLabelInterval = 0; + } + } + if (autoLabelInterval === 0 && step > 1) { + return step; + } + return (autoLabelInterval + 1) * step - 1; + }; + + /** + * @param {Object} axis + * @param {Function} labelFormatter + * @return {Array.} + */ + axisHelper.getFormattedLabels = function (axis, labelFormatter) { + var scale = axis.scale; + var labels = scale.getTicksLabels(); + var ticks = scale.getTicks(); + if (typeof labelFormatter === 'string') { + labelFormatter = (function (tpl) { + return function (val) { + return tpl.replace('{value}', val); + }; + })(labelFormatter); + return zrUtil.map(labels, labelFormatter); + } + else if (typeof labelFormatter === 'function') { + return zrUtil.map(ticks, function (tick, idx) { + return labelFormatter( + axis.type === 'category' ? scale.getLabel(tick) : tick, + idx + ); + }, this); + } + else { + return labels; + } + }; + + module.exports = axisHelper; + + +/***/ }, +/* 115 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Linear continuous scale + * @module echarts/coord/scale/Ordinal + * + * http://en.wikipedia.org/wiki/Level_of_measurement + */ + + // FIXME only one data + + + var zrUtil = __webpack_require__(4); + var Scale = __webpack_require__(116); + + var scaleProto = Scale.prototype; + + var OrdinalScale = Scale.extend({ + + type: 'ordinal', + + init: function (data, extent) { + this._data = data; + this._extent = extent || [0, data.length - 1]; + }, + + parse: function (val) { + return typeof val === 'string' + ? zrUtil.indexOf(this._data, val) + // val might be float. + : Math.round(val); + }, + + contain: function (rank) { + rank = this.parse(rank); + return scaleProto.contain.call(this, rank) + && this._data[rank] != null; + }, + + /** + * Normalize given rank or name to linear [0, 1] + * @param {number|string} [val] + * @return {number} + */ + normalize: function (val) { + return scaleProto.normalize.call(this, this.parse(val)); + }, + + scale: function (val) { + return Math.round(scaleProto.scale.call(this, val)); + }, + + /** + * @return {Array} + */ + getTicks: function () { + var ticks = []; + var extent = this._extent; + var rank = extent[0]; + + while (rank <= extent[1]) { + ticks.push(rank); + rank++; + } + + return ticks; + }, + + /** + * Get item on rank n + * @param {number} n + * @return {string} + */ + getLabel: function (n) { + return this._data[n]; + }, + + /** + * @return {number} + */ + count: function () { + return this._extent[1] - this._extent[0] + 1; + }, + + niceTicks: zrUtil.noop, + niceExtent: zrUtil.noop + }); + + /** + * @return {module:echarts/scale/Time} + */ + OrdinalScale.create = function () { + return new OrdinalScale(); + }; + + module.exports = OrdinalScale; + + +/***/ }, +/* 116 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * // Scale class management + * @module echarts/scale/Scale + */ + + + var clazzUtil = __webpack_require__(13); + + function Scale() { + /** + * Extent + * @type {Array.} + * @protected + */ + this._extent = [Infinity, -Infinity]; + + /** + * Step is calculated in adjustExtent + * @type {Array.} + * @protected + */ + this._interval = 0; + + this.init && this.init.apply(this, arguments); + } + + var scaleProto = Scale.prototype; + + /** + * Parse input val to valid inner number. + * @param {*} val + * @return {number} + */ + scaleProto.parse = function (val) { + // Notice: This would be a trap here, If the implementation + // of this method depends on extent, and this method is used + // before extent set (like in dataZoom), it would be wrong. + // Nevertheless, parse does not depend on extent generally. + return val; + }; + + scaleProto.contain = function (val) { + var extent = this._extent; + return val >= extent[0] && val <= extent[1]; + }; + + /** + * Normalize value to linear [0, 1], return 0.5 if extent span is 0 + * @param {number} val + * @return {number} + */ + scaleProto.normalize = function (val) { + var extent = this._extent; + if (extent[1] === extent[0]) { + return 0.5; + } + return (val - extent[0]) / (extent[1] - extent[0]); + }; + + /** + * Scale normalized value + * @param {number} val + * @return {number} + */ + scaleProto.scale = function (val) { + var extent = this._extent; + return val * (extent[1] - extent[0]) + extent[0]; + }; + + /** + * Set extent from data + * @param {Array.} other + */ + scaleProto.unionExtent = function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + // not setExtent because in log axis it may transformed to power + // this.setExtent(extent[0], extent[1]); + }; + + /** + * Get extent + * @return {Array.} + */ + scaleProto.getExtent = function () { + return this._extent.slice(); + }; + + /** + * Set extent + * @param {number} start + * @param {number} end + */ + scaleProto.setExtent = function (start, end) { + var thisExtent = this._extent; + if (!isNaN(start)) { + thisExtent[0] = start; + } + if (!isNaN(end)) { + thisExtent[1] = end; + } + }; + + /** + * @return {Array.} + */ + scaleProto.getTicksLabels = function () { + var labels = []; + var ticks = this.getTicks(); + for (var i = 0; i < ticks.length; i++) { + labels.push(this.getLabel(ticks[i])); + } + return labels; + }; + + clazzUtil.enableClassExtend(Scale); + clazzUtil.enableClassManagement(Scale, { + registerWhenExtend: true + }); + + module.exports = Scale; + + +/***/ }, +/* 117 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Interval scale + * @module echarts/scale/Interval + */ + + + + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var Scale = __webpack_require__(116); + + var mathFloor = Math.floor; + var mathCeil = Math.ceil; + + var getPrecisionSafe = numberUtil.getPrecisionSafe; + var roundingErrorFix = numberUtil.round; + /** + * @alias module:echarts/coord/scale/Interval + * @constructor + */ + var IntervalScale = Scale.extend({ + + type: 'interval', + + _interval: 0, + + setExtent: function (start, end) { + var thisExtent = this._extent; + //start,end may be a Number like '25',so... + if (!isNaN(start)) { + thisExtent[0] = parseFloat(start); + } + if (!isNaN(end)) { + thisExtent[1] = parseFloat(end); + } + }, + + unionExtent: function (other) { + var extent = this._extent; + other[0] < extent[0] && (extent[0] = other[0]); + other[1] > extent[1] && (extent[1] = other[1]); + + // unionExtent may called by it's sub classes + IntervalScale.prototype.setExtent.call(this, extent[0], extent[1]); + }, + /** + * Get interval + */ + getInterval: function () { + if (!this._interval) { + this.niceTicks(); + } + return this._interval; + }, + + /** + * Set interval + */ + setInterval: function (interval) { + this._interval = interval; + // Dropped auto calculated niceExtent and use user setted extent + // We assume user wan't to set both interval, min, max to get a better result + this._niceExtent = this._extent.slice(); + }, + + /** + * @return {Array.} + */ + getTicks: function () { + if (!this._interval) { + this.niceTicks(); + } + var interval = this._interval; + var extent = this._extent; + var ticks = []; + + // Consider this case: using dataZoom toolbox, zoom and zoom. + var safeLimit = 10000; + + if (interval) { + var niceExtent = this._niceExtent; + var precision = getPrecisionSafe(interval) + 2; + + if (extent[0] < niceExtent[0]) { + ticks.push(extent[0]); + } + var tick = niceExtent[0]; + while (tick <= niceExtent[1]) { + ticks.push(tick); + // Avoid rounding error + tick = roundingErrorFix(tick + interval, precision); + if (ticks.length > safeLimit) { + return []; + } + } + if (extent[1] > niceExtent[1]) { + ticks.push(extent[1]); + } + } + + return ticks; + }, + + /** + * @return {Array.} + */ + getTicksLabels: function () { + var labels = []; + var ticks = this.getTicks(); + for (var i = 0; i < ticks.length; i++) { + labels.push(this.getLabel(ticks[i])); + } + return labels; + }, + + /** + * @param {number} n + * @return {number} + */ + getLabel: function (data) { + return formatUtil.addCommas(data); + }, + + /** + * Update interval and extent of intervals for nice ticks + * + * @param {number} [splitNumber = 5] Desired number of ticks + */ + niceTicks: function (splitNumber) { + splitNumber = splitNumber || 5; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (!isFinite(span)) { + return; + } + // User may set axis min 0 and data are all negative + // FIXME If it needs to reverse ? + if (span < 0) { + span = -span; + extent.reverse(); + } + + // From "Nice Numbers for Graph Labels" of Graphic Gems + // var niceSpan = numberUtil.nice(span, false); + var step = roundingErrorFix( + numberUtil.nice(span / splitNumber, true), + Math.max( + getPrecisionSafe(extent[0]), + getPrecisionSafe(extent[1]) + // extent may be [0, 1], and step should have 1 more digits. + // To make it safe we add 2 more digits + ) + 2 + ); + + var precision = getPrecisionSafe(step) + 2; + // Niced extent inside original extent + var niceExtent = [ + roundingErrorFix(mathCeil(extent[0] / step) * step, precision), + roundingErrorFix(mathFloor(extent[1] / step) * step, precision) + ]; + + this._interval = step; + this._niceExtent = niceExtent; + }, + + /** + * Nice extent. + * @param {number} [splitNumber = 5] Given approx tick number + * @param {boolean} [fixMin=false] + * @param {boolean} [fixMax=false] + */ + niceExtent: function (splitNumber, fixMin, fixMax) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + if (extent[0] !== 0) { + // Expand extent + var expandSize = extent[0]; + // In the fowllowing case + // Axis has been fixed max 100 + // Plus data are all 100 and axis extent are [100, 100]. + // Extend to the both side will cause expanded max is larger than fixed max. + // So only expand to the smaller side. + if (!fixMax) { + extent[1] += expandSize / 2; + extent[0] -= expandSize / 2; + } + else { + extent[0] -= expandSize / 2; + } + } + else { + extent[1] = 1; + } + } + var span = extent[1] - extent[0]; + // If there are no data and extent are [Infinity, -Infinity] + if (!isFinite(span)) { + extent[0] = 0; + extent[1] = 1; + } + + this.niceTicks(splitNumber); + + // var extent = this._extent; + var interval = this._interval; + + if (!fixMin) { + extent[0] = roundingErrorFix(mathFloor(extent[0] / interval) * interval); + } + if (!fixMax) { + extent[1] = roundingErrorFix(mathCeil(extent[1] / interval) * interval); + } + } + }); + + /** + * @return {module:echarts/scale/Time} + */ + IntervalScale.create = function () { + return new IntervalScale(); + }; + + module.exports = IntervalScale; + + + +/***/ }, +/* 118 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Interval scale + * @module echarts/coord/scale/Time + */ + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + + var IntervalScale = __webpack_require__(117); + + var intervalScaleProto = IntervalScale.prototype; + + var mathCeil = Math.ceil; + var mathFloor = Math.floor; + var ONE_SECOND = 1000; + var ONE_MINUTE = ONE_SECOND * 60; + var ONE_HOUR = ONE_MINUTE * 60; + var ONE_DAY = ONE_HOUR * 24; + + // FIXME 公用? + var bisect = function (a, x, lo, hi) { + while (lo < hi) { + var mid = lo + hi >>> 1; + if (a[mid][2] < x) { + lo = mid + 1; + } + else { + hi = mid; + } + } + return lo; + }; + + /** + * @alias module:echarts/coord/scale/Time + * @constructor + */ + var TimeScale = IntervalScale.extend({ + type: 'time', + + // Overwrite + getLabel: function (val) { + var stepLvl = this._stepLvl; + + var date = new Date(val); + + return formatUtil.formatTime(stepLvl[0], date); + }, + + // Overwrite + niceExtent: function (approxTickNum, fixMin, fixMax) { + var extent = this._extent; + // If extent start and end are same, expand them + if (extent[0] === extent[1]) { + // Expand extent + extent[0] -= ONE_DAY; + extent[1] += ONE_DAY; + } + // If there are no data and extent are [Infinity, -Infinity] + if (extent[1] === -Infinity && extent[0] === Infinity) { + var d = new Date(); + extent[1] = new Date(d.getFullYear(), d.getMonth(), d.getDate()); + extent[0] = extent[1] - ONE_DAY; + } + + this.niceTicks(approxTickNum); + + // var extent = this._extent; + var interval = this._interval; + + if (!fixMin) { + extent[0] = numberUtil.round(mathFloor(extent[0] / interval) * interval); + } + if (!fixMax) { + extent[1] = numberUtil.round(mathCeil(extent[1] / interval) * interval); + } + }, + + // Overwrite + niceTicks: function (approxTickNum) { + approxTickNum = approxTickNum || 10; + + var extent = this._extent; + var span = extent[1] - extent[0]; + var approxInterval = span / approxTickNum; + var scaleLevelsLen = scaleLevels.length; + var idx = bisect(scaleLevels, approxInterval, 0, scaleLevelsLen); + + var level = scaleLevels[Math.min(idx, scaleLevelsLen - 1)]; + var interval = level[2]; + // Same with interval scale if span is much larger than 1 year + if (level[0] === 'year') { + var yearSpan = span / interval; + + // From "Nice Numbers for Graph Labels" of Graphic Gems + // var niceYearSpan = numberUtil.nice(yearSpan, false); + var yearStep = numberUtil.nice(yearSpan / approxTickNum, true); + + interval *= yearStep; + } + + var niceExtent = [ + mathCeil(extent[0] / interval) * interval, + mathFloor(extent[1] / interval) * interval + ]; + + this._stepLvl = level; + // Interval will be used in getTicks + this._interval = interval; + this._niceExtent = niceExtent; + }, + + parse: function (val) { + // val might be float. + return +numberUtil.parseDate(val); + } + }); + + zrUtil.each(['contain', 'normalize'], function (methodName) { + TimeScale.prototype[methodName] = function (val) { + return intervalScaleProto[methodName].call(this, this.parse(val)); + }; + }); + + // Steps from d3 + var scaleLevels = [ + // Format step interval + ['hh:mm:ss', 1, ONE_SECOND], // 1s + ['hh:mm:ss', 5, ONE_SECOND * 5], // 5s + ['hh:mm:ss', 10, ONE_SECOND * 10], // 10s + ['hh:mm:ss', 15, ONE_SECOND * 15], // 15s + ['hh:mm:ss', 30, ONE_SECOND * 30], // 30s + ['hh:mm\nMM-dd',1, ONE_MINUTE], // 1m + ['hh:mm\nMM-dd',5, ONE_MINUTE * 5], // 5m + ['hh:mm\nMM-dd',10, ONE_MINUTE * 10], // 10m + ['hh:mm\nMM-dd',15, ONE_MINUTE * 15], // 15m + ['hh:mm\nMM-dd',30, ONE_MINUTE * 30], // 30m + ['hh:mm\nMM-dd',1, ONE_HOUR], // 1h + ['hh:mm\nMM-dd',2, ONE_HOUR * 2], // 2h + ['hh:mm\nMM-dd',6, ONE_HOUR * 6], // 6h + ['hh:mm\nMM-dd',12, ONE_HOUR * 12], // 12h + ['MM-dd\nyyyy', 1, ONE_DAY], // 1d + ['week', 7, ONE_DAY * 7], // 7d + ['month', 1, ONE_DAY * 31], // 1M + ['quarter', 3, ONE_DAY * 380 / 4], // 3M + ['half-year', 6, ONE_DAY * 380 / 2], // 6M + ['year', 1, ONE_DAY * 380] // 1Y + ]; + + /** + * @return {module:echarts/scale/Time} + */ + TimeScale.create = function () { + return new TimeScale(); + }; + + module.exports = TimeScale; + + +/***/ }, +/* 119 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Log scale + * @module echarts/scale/Log + */ + + + var zrUtil = __webpack_require__(4); + var Scale = __webpack_require__(116); + var numberUtil = __webpack_require__(7); + + // Use some method of IntervalScale + var IntervalScale = __webpack_require__(117); + + var scaleProto = Scale.prototype; + var intervalScaleProto = IntervalScale.prototype; + + var mathFloor = Math.floor; + var mathCeil = Math.ceil; + var mathPow = Math.pow; + + var mathLog = Math.log; + + var LogScale = Scale.extend({ + + type: 'log', + + base: 10, + + /** + * @return {Array.} + */ + getTicks: function () { + return zrUtil.map(intervalScaleProto.getTicks.call(this), function (val) { + return numberUtil.round(mathPow(this.base, val)); + }, this); + }, + + /** + * @param {number} val + * @return {string} + */ + getLabel: intervalScaleProto.getLabel, + + /** + * @param {number} val + * @return {number} + */ + scale: function (val) { + val = scaleProto.scale.call(this, val); + return mathPow(this.base, val); + }, + + /** + * @param {number} start + * @param {number} end + */ + setExtent: function (start, end) { + var base = this.base; + start = mathLog(start) / mathLog(base); + end = mathLog(end) / mathLog(base); + intervalScaleProto.setExtent.call(this, start, end); + }, + + /** + * @return {number} end + */ + getExtent: function () { + var base = this.base; + var extent = scaleProto.getExtent.call(this); + extent[0] = mathPow(base, extent[0]); + extent[1] = mathPow(base, extent[1]); + return extent; + }, + + /** + * @param {Array.} extent + */ + unionExtent: function (extent) { + var base = this.base; + extent[0] = mathLog(extent[0]) / mathLog(base); + extent[1] = mathLog(extent[1]) / mathLog(base); + scaleProto.unionExtent.call(this, extent); + }, + + /** + * Update interval and extent of intervals for nice ticks + * @param {number} [approxTickNum = 10] Given approx tick number + */ + niceTicks: function (approxTickNum) { + approxTickNum = approxTickNum || 10; + var extent = this._extent; + var span = extent[1] - extent[0]; + if (span === Infinity || span <= 0) { + return; + } + + var interval = numberUtil.quantity(span); + var err = approxTickNum / span * interval; + + // Filter ticks to get closer to the desired count. + if (err <= 0.5) { + interval *= 10; + } + + // Interval should be integer + while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { + interval *= 10; + } + + var niceExtent = [ + numberUtil.round(mathCeil(extent[0] / interval) * interval), + numberUtil.round(mathFloor(extent[1] / interval) * interval) + ]; + + this._interval = interval; + this._niceExtent = niceExtent; + }, + + /** + * Nice extent. + * @param {number} [approxTickNum = 10] Given approx tick number + * @param {boolean} [fixMin=false] + * @param {boolean} [fixMax=false] + */ + niceExtent: intervalScaleProto.niceExtent + }); + + zrUtil.each(['contain', 'normalize'], function (methodName) { + LogScale.prototype[methodName] = function (val) { + val = mathLog(val) / mathLog(this.base); + return scaleProto[methodName].call(this, val); + }; + }); + + LogScale.create = function () { + return new LogScale(); + }; + + module.exports = LogScale; + + +/***/ }, +/* 120 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Cartesian = __webpack_require__(121); + + function Cartesian2D(name) { + + Cartesian.call(this, name); + } + + Cartesian2D.prototype = { + + constructor: Cartesian2D, + + type: 'cartesian2d', + + /** + * @type {Array.} + * @readOnly + */ + dimensions: ['x', 'y'], + + /** + * Base axis will be used on stacking. + * + * @return {module:echarts/coord/cartesian/Axis2D} + */ + getBaseAxis: function () { + return this.getAxesByScale('ordinal')[0] + || this.getAxesByScale('time')[0] + || this.getAxis('x'); + }, + + /** + * If contain point + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var axisX = this.getAxis('x'); + var axisY = this.getAxis('y'); + return axisX.contain(axisX.toLocalCoord(point[0])) + && axisY.contain(axisY.toLocalCoord(point[1])); + }, + + /** + * If contain data + * @param {Array.} data + * @return {boolean} + */ + containData: function (data) { + return this.getAxis('x').containData(data[0]) + && this.getAxis('y').containData(data[1]); + }, + + /** + * Convert series data to an array of points + * @param {module:echarts/data/List} data + * @param {boolean} stack + * @return {Array} + * Return array of points. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data, stack) { + return data.mapArray(['x', 'y'], function (x, y) { + return this.dataToPoint([x, y]); + }, stack, this); + }, + + /** + * @param {Array.} data + * @param {boolean} [clamp=false] + * @return {Array.} + */ + dataToPoint: function (data, clamp) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + return [ + xAxis.toGlobalCoord(xAxis.dataToCoord(data[0], clamp)), + yAxis.toGlobalCoord(yAxis.dataToCoord(data[1], clamp)) + ]; + }, + + /** + * @param {Array.} point + * @param {boolean} [clamp=false] + * @return {Array.} + */ + pointToData: function (point, clamp) { + var xAxis = this.getAxis('x'); + var yAxis = this.getAxis('y'); + return [ + xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp), + yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp) + ]; + }, + + /** + * Get other axis + * @param {module:echarts/coord/cartesian/Axis2D} axis + */ + getOtherAxis: function (axis) { + return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); + } + }; + + zrUtil.inherits(Cartesian2D, Cartesian); + + module.exports = Cartesian2D; + + +/***/ }, +/* 121 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Cartesian coordinate system + * @module echarts/coord/Cartesian + * + */ + + + var zrUtil = __webpack_require__(4); + + function dimAxisMapper(dim) { + return this._axes[dim]; + } + + /** + * @alias module:echarts/coord/Cartesian + * @constructor + */ + var Cartesian = function (name) { + this._axes = {}; + + this._dimList = []; + + /** + * @type {string} + */ + this.name = name || ''; + }; + + Cartesian.prototype = { + + constructor: Cartesian, + + type: 'cartesian', + + /** + * Get axis + * @param {number|string} dim + * @return {module:echarts/coord/Cartesian~Axis} + */ + getAxis: function (dim) { + return this._axes[dim]; + }, + + /** + * Get axes list + * @return {Array.} + */ + getAxes: function () { + return zrUtil.map(this._dimList, dimAxisMapper, this); + }, + + /** + * Get axes list by given scale type + */ + getAxesByScale: function (scaleType) { + scaleType = scaleType.toLowerCase(); + return zrUtil.filter( + this.getAxes(), + function (axis) { + return axis.scale.type === scaleType; + } + ); + }, + + /** + * Add axis + * @param {module:echarts/coord/Cartesian.Axis} + */ + addAxis: function (axis) { + var dim = axis.dim; + + this._axes[dim] = axis; + + this._dimList.push(dim); + }, + + /** + * Convert data to coord in nd space + * @param {Array.|Object.} val + * @return {Array.|Object.} + */ + dataToCoord: function (val) { + return this._dataCoordConvert(val, 'dataToCoord'); + }, + + /** + * Convert coord in nd space to data + * @param {Array.|Object.} val + * @return {Array.|Object.} + */ + coordToData: function (val) { + return this._dataCoordConvert(val, 'coordToData'); + }, + + _dataCoordConvert: function (input, method) { + var dimList = this._dimList; + + var output = input instanceof Array ? [] : {}; + + for (var i = 0; i < dimList.length; i++) { + var dim = dimList[i]; + var axis = this._axes[dim]; + + output[dim] = axis[method](input[dim]); + } + + return output; + } + }; + + module.exports = Cartesian; + + +/***/ }, +/* 122 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisLabelInterval = __webpack_require__(124); + + /** + * Extend axis 2d + * @constructor module:echarts/coord/cartesian/Axis2D + * @extends {module:echarts/coord/cartesian/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var Axis2D = function (dim, scale, coordExtent, axisType, position) { + Axis.call(this, dim, scale, coordExtent); + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * Axis position + * - 'top' + * - 'bottom' + * - 'left' + * - 'right' + */ + this.position = position || 'bottom'; + }; + + Axis2D.prototype = { + + constructor: Axis2D, + + /** + * Index of axis, can be used as key + */ + index: 0, + /** + * If axis is on the zero position of the other axis + * @type {boolean} + */ + onZero: false, + + /** + * Axis model + * @param {module:echarts/coord/cartesian/AxisModel} + */ + model: null, + + isHorizontal: function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + }, + + getGlobalExtent: function () { + var ret = this.getExtent(); + ret[0] = this.toGlobalCoord(ret[0]); + ret[1] = this.toGlobalCoord(ret[1]); + return ret; + }, + + /** + * @return {number} + */ + getLabelInterval: function () { + var labelInterval = this._labelInterval; + if (!labelInterval) { + labelInterval = this._labelInterval = axisLabelInterval(this); + } + return labelInterval; + }, + + /** + * If label is ignored. + * Automatically used when axis is category and label can not be all shown + * @param {number} idx + * @return {boolean} + */ + isLabelIgnored: function (idx) { + if (this.type === 'category') { + var labelInterval = this.getLabelInterval(); + return ((typeof labelInterval === 'function') + && !labelInterval(idx, this.scale.getLabel(idx))) + || idx % (labelInterval + 1); + } + }, + + /** + * Transform global coord to local coord, + * i.e. var localCoord = axis.toLocalCoord(80); + * designate by module:echarts/coord/cartesian/Grid. + * @type {Function} + */ + toLocalCoord: null, + + /** + * Transform global coord to local coord, + * i.e. var globalCoord = axis.toLocalCoord(40); + * designate by module:echarts/coord/cartesian/Grid. + * @type {Function} + */ + toGlobalCoord: null + + }; + zrUtil.inherits(Axis2D, Axis); + + module.exports = Axis2D; + + +/***/ }, +/* 123 */ +/***/ function(module, exports, __webpack_require__) { + + + + var numberUtil = __webpack_require__(7); + var linearMap = numberUtil.linearMap; + var zrUtil = __webpack_require__(4); + + function fixExtentWithBands(extent, nTick) { + var size = extent[1] - extent[0]; + var len = nTick; + var margin = size / len / 2; + extent[0] += margin; + extent[1] -= margin; + } + + var normalizedExtent = [0, 1]; + /** + * @name module:echarts/coord/CartesianAxis + * @constructor + */ + var Axis = function (dim, scale, extent) { + + /** + * Axis dimension. Such as 'x', 'y', 'z', 'angle', 'radius' + * @type {string} + */ + this.dim = dim; + + /** + * Axis scale + * @type {module:echarts/coord/scale/*} + */ + this.scale = scale; + + /** + * @type {Array.} + * @private + */ + this._extent = extent || [0, 0]; + + /** + * @type {boolean} + */ + this.inverse = false; + + /** + * Usually true when axis has a ordinal scale + * @type {boolean} + */ + this.onBand = false; + }; + + Axis.prototype = { + + constructor: Axis, + + /** + * If axis extent contain given coord + * @param {number} coord + * @return {boolean} + */ + contain: function (coord) { + var extent = this._extent; + var min = Math.min(extent[0], extent[1]); + var max = Math.max(extent[0], extent[1]); + return coord >= min && coord <= max; + }, + + /** + * If axis extent contain given data + * @param {number} data + * @return {boolean} + */ + containData: function (data) { + return this.contain(this.dataToCoord(data)); + }, + + /** + * Get coord extent. + * @return {Array.} + */ + getExtent: function () { + var ret = this._extent.slice(); + return ret; + }, + + /** + * Get precision used for formatting + * @param {Array.} [dataExtent] + * @return {number} + */ + getPixelPrecision: function (dataExtent) { + return numberUtil.getPixelPrecision( + dataExtent || this.scale.getExtent(), + this._extent + ); + }, + + /** + * Set coord extent + * @param {number} start + * @param {number} end + */ + setExtent: function (start, end) { + var extent = this._extent; + extent[0] = start; + extent[1] = end; + }, + + /** + * Convert data to coord. Data is the rank if it has a ordinal scale + * @param {number} data + * @param {boolean} clamp + * @return {number} + */ + dataToCoord: function (data, clamp) { + var extent = this._extent; + var scale = this.scale; + data = scale.normalize(data); + + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + + return linearMap(data, normalizedExtent, extent, clamp); + }, + + /** + * Convert coord to data. Data is the rank if it has a ordinal scale + * @param {number} coord + * @param {boolean} clamp + * @return {number} + */ + coordToData: function (coord, clamp) { + var extent = this._extent; + var scale = this.scale; + + if (this.onBand && scale.type === 'ordinal') { + extent = extent.slice(); + fixExtentWithBands(extent, scale.count()); + } + + var t = linearMap(coord, extent, normalizedExtent, clamp); + + return this.scale.scale(t); + }, + /** + * @return {Array.} + */ + getTicksCoords: function (alignWithLabel) { + if (this.onBand && !alignWithLabel) { + var bands = this.getBands(); + var coords = []; + for (var i = 0; i < bands.length; i++) { + coords.push(bands[i][0]); + } + if (bands[i - 1]) { + coords.push(bands[i - 1][1]); + } + return coords; + } + else { + return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this); + } + }, + + /** + * Coords of labels are on the ticks or on the middle of bands + * @return {Array.} + */ + getLabelsCoords: function () { + return zrUtil.map(this.scale.getTicks(), this.dataToCoord, this); + }, + + /** + * Get bands. + * + * If axis has labels [1, 2, 3, 4]. Bands on the axis are + * |---1---|---2---|---3---|---4---|. + * + * @return {Array} + */ + // FIXME Situation when labels is on ticks + getBands: function () { + var extent = this.getExtent(); + var bands = []; + var len = this.scale.count(); + var start = extent[0]; + var end = extent[1]; + var span = end - start; + + for (var i = 0; i < len; i++) { + bands.push([ + span * i / len + start, + span * (i + 1) / len + start + ]); + } + return bands; + }, + + /** + * Get width of band + * @return {number} + */ + getBandWidth: function () { + var axisExtent = this._extent; + var dataExtent = this.scale.getExtent(); + + var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); + // Fix #2728, avoid NaN when only one data. + len === 0 && (len = 1); + + var size = Math.abs(axisExtent[1] - axisExtent[0]); + + return Math.abs(size) / len; + } + }; + + module.exports = Axis; + + +/***/ }, +/* 124 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Helper function for axisLabelInterval calculation + */ + + + + var zrUtil = __webpack_require__(4); + var axisHelper = __webpack_require__(114); + + module.exports = function (axis) { + var axisModel = axis.model; + var labelModel = axisModel.getModel('axisLabel'); + var labelInterval = labelModel.get('interval'); + if (!(axis.type === 'category' && labelInterval === 'auto')) { + return labelInterval === 'auto' ? 0 : labelInterval; + } + + return axisHelper.getAxisLabelInterval( + zrUtil.map(axis.scale.getTicks(), axis.dataToCoord, axis), + axisModel.getFormattedLabels(), + labelModel.getModel('textStyle').getFont(), + axis.isHorizontal() + ); + }; + + +/***/ }, +/* 125 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // Grid 是在有直角坐标系的时候必须要存在的 + // 所以这里也要被 Cartesian2D 依赖 + + + __webpack_require__(126); + var ComponentModel = __webpack_require__(19); + + module.exports = ComponentModel.extend({ + + type: 'grid', + + dependencies: ['xAxis', 'yAxis'], + + layoutMode: 'box', + + /** + * @type {module:echarts/coord/cartesian/Grid} + */ + coordinateSystem: null, + + defaultOption: { + show: false, + zlevel: 0, + z: 0, + left: '10%', + top: 60, + right: '10%', + bottom: 60, + // If grid size contain label + containLabel: false, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 1, + borderColor: '#ccc' + } + }); + + +/***/ }, +/* 126 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var ComponentModel = __webpack_require__(19); + var zrUtil = __webpack_require__(4); + var axisModelCreator = __webpack_require__(127); + + var AxisModel = ComponentModel.extend({ + + type: 'cartesian2dAxis', + + /** + * @type {module:echarts/coord/cartesian/Axis2D} + */ + axis: null, + + /** + * @override + */ + init: function () { + AxisModel.superApply(this, 'init', arguments); + this._resetRange(); + }, + + /** + * @override + */ + mergeOption: function () { + AxisModel.superApply(this, 'mergeOption', arguments); + this._resetRange(); + }, + + /** + * @override + */ + restoreData: function () { + AxisModel.superApply(this, 'restoreData', arguments); + this._resetRange(); + }, + + /** + * @public + * @param {number} rangeStart + * @param {number} rangeEnd + */ + setRange: function (rangeStart, rangeEnd) { + this.option.rangeStart = rangeStart; + this.option.rangeEnd = rangeEnd; + }, + + /** + * @public + * @return {Array.} + */ + getMin: function () { + var option = this.option; + return option.rangeStart != null ? option.rangeStart : option.min; + }, + + /** + * @public + * @return {Array.} + */ + getMax: function () { + var option = this.option; + return option.rangeEnd != null ? option.rangeEnd : option.max; + }, + + /** + * @public + * @return {boolean} + */ + getNeedCrossZero: function () { + var option = this.option; + return (option.rangeStart != null || option.rangeEnd != null) + ? false : !option.scale; + }, + + /** + * @return {module:echarts/model/Model} + */ + findGridModel: function () { + return this.ecModel.queryComponents({ + mainType: 'grid', + index: this.get('gridIndex'), + id: this.get('gridId') + })[0]; + }, + + /** + * @private + */ + _resetRange: function () { + // rangeStart and rangeEnd is readonly. + this.option.rangeStart = this.option.rangeEnd = null; + } + + }); + + function getAxisType(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); + } + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + var extraOption = { + // gridIndex: 0, + // gridId: '', + + // Offset is for multiple axis on the same position + offset: 0 + }; + + axisModelCreator('x', AxisModel, getAxisType, extraOption); + axisModelCreator('y', AxisModel, getAxisType, extraOption); + + module.exports = AxisModel; + + +/***/ }, +/* 127 */ +/***/ function(module, exports, __webpack_require__) { + + + + var axisDefault = __webpack_require__(128); + var zrUtil = __webpack_require__(4); + var ComponentModel = __webpack_require__(19); + var layout = __webpack_require__(21); + + // FIXME axisType is fixed ? + var AXIS_TYPES = ['value', 'category', 'time', 'log']; + + /** + * Generate sub axis model class + * @param {string} axisName 'x' 'y' 'radius' 'angle' 'parallel' + * @param {module:echarts/model/Component} BaseAxisModelClass + * @param {Function} axisTypeDefaulter + * @param {Object} [extraDefaultOption] + */ + module.exports = function (axisName, BaseAxisModelClass, axisTypeDefaulter, extraDefaultOption) { + + zrUtil.each(AXIS_TYPES, function (axisType) { + + BaseAxisModelClass.extend({ + + type: axisName + 'Axis.' + axisType, + + mergeDefaultAndTheme: function (option, ecModel) { + var layoutMode = this.layoutMode; + var inputPositionParams = layoutMode + ? layout.getLayoutParams(option) : {}; + + var themeModel = ecModel.getTheme(); + zrUtil.merge(option, themeModel.get(axisType + 'Axis')); + zrUtil.merge(option, this.getDefaultOption()); + + option.type = axisTypeDefaulter(axisName, option); + + if (layoutMode) { + layout.mergeLayoutParam(option, inputPositionParams, layoutMode); + } + }, + + defaultOption: zrUtil.mergeAll( + [ + {}, + axisDefault[axisType + 'Axis'], + extraDefaultOption + ], + true + ) + }); + }); + + ComponentModel.registerSubTypeDefaulter( + axisName + 'Axis', + zrUtil.curry(axisTypeDefaulter, axisName) + ); + }; + + +/***/ }, +/* 128 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var defaultOption = { + show: true, + zlevel: 0, // 一级层叠 + z: 0, // 二级层叠 + // 反向坐标轴 + inverse: false, + + // 坐标轴名字,默认为空 + name: '', + // 坐标轴名字位置,支持'start' | 'middle' | 'end' + nameLocation: 'end', + // 坐标轴名字旋转,degree。 + nameRotate: null, // Adapt to axis rotate, when nameLocation is 'middle'. + nameTruncate: { + maxWidth: null, + ellipsis: '...', + placeholder: '.' + }, + // 坐标轴文字样式,默认取全局样式 + nameTextStyle: {}, + // 文字与轴线距离 + nameGap: 15, + + silent: false, // Default false to support tooltip. + triggerEvent: false, // Default false to avoid legacy user event listener fail. + + tooltip: { + show: false + }, + + // 坐标轴线 + axisLine: { + // 默认显示,属性show控制显示与否 + show: true, + onZero: true, + // 属性lineStyle控制线条样式 + lineStyle: { + color: '#333', + width: 1, + type: 'solid' + } + }, + // 坐标轴小标记 + axisTick: { + // 属性show控制显示与否,默认显示 + show: true, + // 控制小标记是否在grid里 + inside: false, + // 属性length控制线长 + length: 5, + // 属性lineStyle控制线条样式 + lineStyle: { + width: 1 + } + }, + // 坐标轴文本标签,详见axis.axisLabel + axisLabel: { + show: true, + // 控制文本标签是否在grid里 + inside: false, + rotate: 0, + margin: 8, + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + fontSize: 12 + } + }, + // 分隔线 + splitLine: { + // 默认显示,属性show控制显示与否 + show: true, + // 属性lineStyle(详见lineStyle)控制线条样式 + lineStyle: { + color: ['#ccc'], + width: 1, + type: 'solid' + } + }, + // 分隔区域 + splitArea: { + // 默认不显示,属性show控制显示与否 + show: false, + // 属性areaStyle(详见areaStyle)控制区域样式 + areaStyle: { + color: ['rgba(250,250,250,0.3)','rgba(200,200,200,0.3)'] + } + } + }; + + var categoryAxis = zrUtil.merge({ + // 类目起始和结束两端空白策略 + boundaryGap: true, + // splitArea: { + // show: false + // }, + splitLine: { + show: false + }, + // 坐标轴小标记 + axisTick: { + // If tick is align with label when boundaryGap is true + // Default with axisTick + alignWithLabel: false, + interval: 'auto' + }, + // 坐标轴文本标签,详见axis.axisLabel + axisLabel: { + interval: 'auto' + } + }, defaultOption); + + var valueAxis = zrUtil.merge({ + // 数值起始和结束两端空白策略 + boundaryGap: [0, 0], + // 最小值, 设置成 'dataMin' 则从数据中计算最小值 + // min: null, + // 最大值,设置成 'dataMax' 则从数据中计算最大值 + // max: null, + // Readonly prop, specifies start value of the range when using data zoom. + // rangeStart: null + // Readonly prop, specifies end value of the range when using data zoom. + // rangeEnd: null + // 脱离0值比例,放大聚焦到最终_min,_max区间 + // scale: false, + // 分割段数,默认为5 + splitNumber: 5 + // Minimum interval + // minInterval: null + }, defaultOption); + + // FIXME + var timeAxis = zrUtil.defaults({ + scale: true, + min: 'dataMin', + max: 'dataMax' + }, valueAxis); + var logAxis = zrUtil.defaults({ + logBase: 10 + }, valueAxis); + logAxis.scale = true; + + module.exports = { + categoryAxis: categoryAxis, + valueAxis: valueAxis, + timeAxis: timeAxis, + logAxis: logAxis + }; + + +/***/ }, +/* 129 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var axisHelper = __webpack_require__(114); + + function getName(obj) { + if (zrUtil.isObject(obj) && obj.value != null) { + return obj.value; + } + else { + return obj; + } + } + /** + * Get categories + */ + function getCategories() { + return this.get('type') === 'category' + && zrUtil.map(this.get('data'), getName); + } + + /** + * Format labels + * @return {Array.} + */ + function getFormattedLabels() { + return axisHelper.getFormattedLabels( + this.axis, + this.get('axisLabel.formatter') + ); + } + + module.exports = { + + getFormattedLabels: getFormattedLabels, + + getCategories: getCategories + }; + + +/***/ }, +/* 130 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // TODO boundaryGap + + + __webpack_require__(126); + + __webpack_require__(131); + + +/***/ }, +/* 131 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var AxisBuilder = __webpack_require__(132); + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick; + var getInterval = AxisBuilder.getInterval; + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + var selfBuilderAttrs = [ + 'splitArea', 'splitLine' + ]; + + // function getAlignWithLabel(model, axisModel) { + // var alignWithLabel = model.get('alignWithLabel'); + // if (alignWithLabel === 'auto') { + // alignWithLabel = axisModel.get('axisTick.alignWithLabel'); + // } + // return alignWithLabel; + // } + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'axis', + + render: function (axisModel, ecModel) { + + this.group.removeAll(); + + var oldAxisGroup = this._axisGroup; + this._axisGroup = new graphic.Group(); + + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var gridModel = axisModel.findGridModel(); + + var layout = layoutAxis(gridModel, axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + zrUtil.each(selfBuilderAttrs, function (name) { + if (axisModel.get(name + '.show')) { + this['_' + name](axisModel, gridModel, layout.labelInterval); + } + }, this); + + graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel); + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {number|Function} labelInterval + * @private + */ + _splitLine: function (axisModel, gridModel, labelInterval) { + var axis = axisModel.axis; + + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + + var lineInterval = getInterval(splitLineModel, labelInterval); + + lineColors = zrUtil.isArray(lineColors) ? lineColors : [lineColors]; + + var gridRect = gridModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords( + // splitLineModel.get('alignWithLabel') + ); + var ticks = axis.scale.getTicks(); + + var p1 = []; + var p2 = []; + // Simple optimization + // Batching the lines if color are the same + var lineStyle = lineStyleModel.getLineStyle(); + for (var i = 0; i < ticksCoords.length; i++) { + if (ifIgnoreOnTick(axis, i, lineInterval)) { + continue; + } + + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } + else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + + var colorIndex = (lineCount++) % lineColors.length; + this._axisGroup.add(new graphic.Line(graphic.subPixelOptimizeLine({ + anid: 'line_' + ticks[i], + + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: zrUtil.defaults({ + stroke: lineColors[colorIndex] + }, lineStyle), + silent: true + }))); + } + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @param {number|Function} labelInterval + * @private + */ + _splitArea: function (axisModel, gridModel, labelInterval) { + var axis = axisModel.axis; + + var splitAreaModel = axisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + + var gridRect = gridModel.coordinateSystem.getRect(); + + var ticksCoords = axis.getTicksCoords( + // splitAreaModel.get('alignWithLabel') + ); + var ticks = axis.scale.getTicks(); + + var prevX = axis.toGlobalCoord(ticksCoords[0]); + var prevY = axis.toGlobalCoord(ticksCoords[0]); + + var count = 0; + + var areaInterval = getInterval(splitAreaModel, labelInterval); + + var areaStyle = areaStyleModel.getAreaStyle(); + areaColors = zrUtil.isArray(areaColors) ? areaColors : [areaColors]; + + for (var i = 1; i < ticksCoords.length; i++) { + if (ifIgnoreOnTick(axis, i, areaInterval)) { + continue; + } + + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + + var x; + var y; + var width; + var height; + if (axis.isHorizontal()) { + x = prevX; + y = gridRect.y; + width = tickCoord - x; + height = gridRect.height; + } + else { + x = gridRect.x; + y = prevY; + width = gridRect.width; + height = tickCoord - y; + } + + var colorIndex = (count++) % areaColors.length; + this._axisGroup.add(new graphic.Rect({ + anid: 'area_' + ticks[i], + + shape: { + x: x, + y: y, + width: width, + height: height + }, + style: zrUtil.defaults({ + fill: areaColors[colorIndex] + }, areaStyle), + silent: true + })); + + prevX = x + width; + prevY = y + height; + } + } + }); + + AxisView.extend({ + type: 'xAxis' + }); + AxisView.extend({ + type: 'yAxis' + }); + + /** + * @inner + */ + function layoutAxis(gridModel, axisModel) { + var grid = gridModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + + var rawAxisPosition = axis.position; + var axisPosition = axis.onZero ? 'onZero' : rawAxisPosition; + var axisDim = axis.dim; + + // [left, right, top, bottom] + var rect = grid.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + + var axisOffset = axisModel.get('offset') || 0; + + var posMap = { + x: { top: rectBound[2] - axisOffset, bottom: rectBound[3] + axisOffset }, + y: { left: rectBound[0] - axisOffset, right: rectBound[1] + axisOffset } + }; + + posMap.x.onZero = Math.max(Math.min(getZero('y'), posMap.x.bottom), posMap.x.top); + posMap.y.onZero = Math.max(Math.min(getZero('x'), posMap.y.right), posMap.y.left); + + function getZero(dim, val) { + var theAxis = grid.getAxis(dim); + return theAxis.toGlobalCoord(theAxis.dataToCoord(0)); + } + + // Axis position + layout.position = [ + axisDim === 'y' ? posMap.y[axisPosition] : rectBound[0], + axisDim === 'x' ? posMap.x[axisPosition] : rectBound[3] + ]; + + // Axis rotation + layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); + + // Tick and label direction, x y is axisDim + var dirMap = {top: -1, bottom: 1, left: -1, right: 1}; + + layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition]; + if (axis.onZero) { + layout.labelOffset = posMap[axisDim][rawAxisPosition] - posMap[axisDim].onZero; + } + + if (axisModel.getModel('axisTick').get('inside')) { + layout.tickDirection = -layout.tickDirection; + } + if (axisModel.getModel('axisLabel').get('inside')) { + layout.labelDirection = -layout.labelDirection; + } + + // Special label rotation + var labelRotation = axisModel.getModel('axisLabel').get('rotate'); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + + // label interval when auto mode. + layout.labelInterval = axis.getLabelInterval(); + + // Over splitLine and splitArea + layout.z2 = 1; + + return layout; + } + + +/***/ }, +/* 132 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + var numberUtil = __webpack_require__(7); + var remRadian = numberUtil.remRadian; + var isRadianAroundZero = numberUtil.isRadianAroundZero; + var vec2 = __webpack_require__(10); + var v2ApplyTransform = vec2.applyTransform; + var retrieve = zrUtil.retrieve; + + var PI = Math.PI; + + function makeAxisEventDataBase(axisModel) { + var eventData = { + componentType: axisModel.mainType + }; + eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex; + return eventData; + } + + /** + * A final axis is translated and rotated from a "standard axis". + * So opt.position and opt.rotation is required. + * + * A standard axis is and axis from [0, 0] to [0, axisExtent[1]], + * for example: (0, 0) ------------> (0, 50) + * + * nameDirection or tickDirection or labelDirection is 1 means tick + * or label is below the standard axis, whereas is -1 means above + * the standard axis. labelOffset means offset between label and axis, + * which is useful when 'onZero', where axisLabel is in the grid and + * label in outside grid. + * + * Tips: like always, + * positive rotation represents anticlockwise, and negative rotation + * represents clockwise. + * The direction of position coordinate is the same as the direction + * of screen coordinate. + * + * Do not need to consider axis 'inverse', which is auto processed by + * axis extent. + * + * @param {module:zrender/container/Group} group + * @param {Object} axisModel + * @param {Object} opt Standard axis parameters. + * @param {Array.} opt.position [x, y] + * @param {number} opt.rotation by radian + * @param {number} [opt.nameDirection=1] 1 or -1 Used when nameLocation is 'middle'. + * @param {number} [opt.tickDirection=1] 1 or -1 + * @param {number} [opt.labelDirection=1] 1 or -1 + * @param {number} [opt.labelOffset=0] Usefull when onZero. + * @param {string} [opt.axisLabelShow] default get from axisModel. + * @param {string} [opt.axisName] default get from axisModel. + * @param {number} [opt.axisNameAvailableWidth] + * @param {number} [opt.labelRotation] by degree, default get from axisModel. + * @param {number} [opt.labelInterval] Default label interval when label + * interval from model is null or 'auto'. + * @param {number} [opt.strokeContainThreshold] Default label interval when label + */ + var AxisBuilder = function (axisModel, opt) { + + /** + * @readOnly + */ + this.opt = opt; + + /** + * @readOnly + */ + this.axisModel = axisModel; + + // Default value + zrUtil.defaults( + opt, + { + labelOffset: 0, + nameDirection: 1, + tickDirection: 1, + labelDirection: 1, + silent: true + } + ); + + /** + * @readOnly + */ + this.group = new graphic.Group(); + + // FIXME Not use a seperate text group? + var dumbGroup = new graphic.Group({ + position: opt.position.slice(), + rotation: opt.rotation + }); + + // this.group.add(dumbGroup); + // this._dumbGroup = dumbGroup; + + dumbGroup.updateTransform(); + this._transform = dumbGroup.transform; + + this._dumbGroup = dumbGroup; + }; + + AxisBuilder.prototype = { + + constructor: AxisBuilder, + + hasBuilder: function (name) { + return !!builders[name]; + }, + + add: function (name) { + builders[name].call(this); + }, + + getGroup: function () { + return this.group; + } + + }; + + var builders = { + + /** + * @private + */ + axisLine: function () { + var opt = this.opt; + var axisModel = this.axisModel; + + if (!axisModel.get('axisLine.show')) { + return; + } + + var extent = this.axisModel.axis.getExtent(); + + var matrix = this._transform; + var pt1 = [extent[0], 0]; + var pt2 = [extent[1], 0]; + if (matrix) { + v2ApplyTransform(pt1, pt1, matrix); + v2ApplyTransform(pt2, pt2, matrix); + } + + this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({ + + // Id for animation + anid: 'line', + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: zrUtil.extend( + {lineCap: 'round'}, + axisModel.getModel('axisLine.lineStyle').getLineStyle() + ), + strokeContainThreshold: opt.strokeContainThreshold || 5, + silent: true, + z2: 1 + }))); + }, + + /** + * @private + */ + axisTick: function () { + var axisModel = this.axisModel; + + if (!axisModel.get('axisTick.show')) { + return; + } + + var axis = axisModel.axis; + var tickModel = axisModel.getModel('axisTick'); + var opt = this.opt; + + var lineStyleModel = tickModel.getModel('lineStyle'); + var tickLen = tickModel.get('length'); + + var tickInterval = getInterval(tickModel, opt.labelInterval); + var ticksCoords = axis.getTicksCoords(tickModel.get('alignWithLabel')); + var ticks = axis.scale.getTicks(); + + var pt1 = []; + var pt2 = []; + var matrix = this._transform; + + for (var i = 0; i < ticksCoords.length; i++) { + // Only ordinal scale support tick interval + if (ifIgnoreOnTick(axis, i, tickInterval)) { + continue; + } + + var tickCoord = ticksCoords[i]; + + pt1[0] = tickCoord; + pt1[1] = 0; + pt2[0] = tickCoord; + pt2[1] = opt.tickDirection * tickLen; + + if (matrix) { + v2ApplyTransform(pt1, pt1, matrix); + v2ApplyTransform(pt2, pt2, matrix); + } + // Tick line, Not use group transform to have better line draw + this.group.add(new graphic.Line(graphic.subPixelOptimizeLine({ + + // Id for animation + anid: 'tick_' + ticks[i], + + shape: { + x1: pt1[0], + y1: pt1[1], + x2: pt2[0], + y2: pt2[1] + }, + style: zrUtil.defaults( + lineStyleModel.getLineStyle(), + { + stroke: axisModel.get('axisLine.lineStyle.color') + } + ), + z2: 2, + silent: true + }))); + } + }, + + /** + * @param {module:echarts/coord/cartesian/AxisModel} axisModel + * @param {module:echarts/coord/cartesian/GridModel} gridModel + * @private + */ + axisLabel: function () { + var opt = this.opt; + var axisModel = this.axisModel; + var show = retrieve(opt.axisLabelShow, axisModel.get('axisLabel.show')); + + if (!show) { + return; + } + + var axis = axisModel.axis; + var labelModel = axisModel.getModel('axisLabel'); + var textStyleModel = labelModel.getModel('textStyle'); + var labelMargin = labelModel.get('margin'); + var ticks = axis.scale.getTicks(); + var labels = axisModel.getFormattedLabels(); + + // Special label rotate. + var labelRotation = retrieve(opt.labelRotation, labelModel.get('rotate')) || 0; + // To radian. + labelRotation = labelRotation * PI / 180; + + var labelLayout = innerTextLayout(opt, labelRotation, opt.labelDirection); + var categoryData = axisModel.get('data'); + + var textEls = []; + var silent = isSilent(axisModel); + var triggerEvent = axisModel.get('triggerEvent'); + + for (var i = 0; i < ticks.length; i++) { + if (ifIgnoreOnTick(axis, i, opt.labelInterval)) { + continue; + } + + var itemTextStyleModel = textStyleModel; + if (categoryData && categoryData[i] && categoryData[i].textStyle) { + itemTextStyleModel = new Model( + categoryData[i].textStyle, textStyleModel, axisModel.ecModel + ); + } + var textColor = itemTextStyleModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'); + + var tickCoord = axis.dataToCoord(ticks[i]); + var pos = [ + tickCoord, + opt.labelOffset + opt.labelDirection * labelMargin + ]; + var labelBeforeFormat = axis.scale.getLabel(ticks[i]); + + var textEl = new graphic.Text({ + + // Id for animation + anid: 'label_' + ticks[i], + + style: { + text: labels[i], + textAlign: itemTextStyleModel.get('align', true) || labelLayout.textAlign, + textVerticalAlign: itemTextStyleModel.get('baseline', true) || labelLayout.verticalAlign, + textFont: itemTextStyleModel.getFont(), + fill: typeof textColor === 'function' ? textColor(labelBeforeFormat) : textColor + }, + position: pos, + rotation: labelLayout.rotation, + silent: silent, + z2: 10 + }); + + // Pack data for mouse event + if (triggerEvent) { + textEl.eventData = makeAxisEventDataBase(axisModel); + textEl.eventData.targetType = 'axisLabel'; + textEl.eventData.value = labelBeforeFormat; + } + + + // FIXME + this._dumbGroup.add(textEl); + textEl.updateTransform(); + + textEls.push(textEl); + this.group.add(textEl); + + textEl.decomposeTransform(); + } + + function isTwoLabelOverlapped(current, next) { + var firstRect = current && current.getBoundingRect().clone(); + var nextRect = next && next.getBoundingRect().clone(); + if (firstRect && nextRect) { + firstRect.applyTransform(current.getLocalTransform()); + nextRect.applyTransform(next.getLocalTransform()); + return firstRect.intersect(nextRect); + } + } + if (axis.type !== 'category') { + // If min or max are user set, we need to check + // If the tick on min(max) are overlap on their neighbour tick + // If they are overlapped, we need to hide the min(max) tick label + if (axisModel.getMin ? axisModel.getMin() : axisModel.get('min')) { + var firstLabel = textEls[0]; + var nextLabel = textEls[1]; + if (isTwoLabelOverlapped(firstLabel, nextLabel)) { + firstLabel.ignore = true; + } + } + if (axisModel.getMax ? axisModel.getMax() : axisModel.get('max')) { + var lastLabel = textEls[textEls.length - 1]; + var prevLabel = textEls[textEls.length - 2]; + if (isTwoLabelOverlapped(prevLabel, lastLabel)) { + lastLabel.ignore = true; + } + } + } + }, + + /** + * @private + */ + axisName: function () { + var opt = this.opt; + var axisModel = this.axisModel; + var name = retrieve(opt.axisName, axisModel.get('name')); + + if (!name) { + return; + } + + var nameLocation = axisModel.get('nameLocation'); + var nameDirection = opt.nameDirection; + var textStyleModel = axisModel.getModel('nameTextStyle'); + var gap = axisModel.get('nameGap') || 0; + + var extent = this.axisModel.axis.getExtent(); + var gapSignal = extent[0] > extent[1] ? -1 : 1; + var pos = [ + nameLocation === 'start' + ? extent[0] - gapSignal * gap + : nameLocation === 'end' + ? extent[1] + gapSignal * gap + : (extent[0] + extent[1]) / 2, // 'middle' + // Reuse labelOffset. + nameLocation === 'middle' ? opt.labelOffset + nameDirection * gap : 0 + ]; + + var labelLayout; + + var nameRotation = axisModel.get('nameRotate'); + if (nameRotation != null) { + nameRotation = nameRotation * PI / 180; // To radian. + } + + var axisNameAvailableWidth; + + if (nameLocation === 'middle') { + labelLayout = innerTextLayout( + opt, + nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis. + nameDirection + ); + } + else { + labelLayout = endTextLayout( + opt, nameLocation, nameRotation || 0, extent + ); + + axisNameAvailableWidth = opt.axisNameAvailableWidth; + if (axisNameAvailableWidth != null) { + axisNameAvailableWidth = Math.abs( + axisNameAvailableWidth / Math.sin(labelLayout.rotation) + ); + !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null); + } + } + + var textFont = textStyleModel.getFont(); + + var truncateOpt = axisModel.get('nameTruncate', true) || {}; + var ellipsis = truncateOpt.ellipsis; + var maxWidth = retrieve(truncateOpt.maxWidth, axisNameAvailableWidth); + var truncatedText = (ellipsis != null && maxWidth != null) + ? formatUtil.truncateText( + name, maxWidth, textFont, ellipsis, + {minChar: 2, placeholder: truncateOpt.placeholder} + ) + : name; + + var tooltipOpt = axisModel.get('tooltip', true); + + var mainType = axisModel.mainType; + var formatterParams = { + componentType: mainType, + name: name, + $vars: ['name'] + }; + formatterParams[mainType + 'Index'] = axisModel.componentIndex; + + var textEl = new graphic.Text({ + + // Id for animation + anid: 'name', + + __fullText: name, + __truncatedText: truncatedText, + + style: { + text: truncatedText, + textFont: textFont, + fill: textStyleModel.getTextColor() + || axisModel.get('axisLine.lineStyle.color'), + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign + }, + position: pos, + rotation: labelLayout.rotation, + silent: isSilent(axisModel), + z2: 1, + tooltip: (tooltipOpt && tooltipOpt.show) + ? zrUtil.extend({ + content: name, + formatter: function () { + return name; + }, + formatterParams: formatterParams + }, tooltipOpt) + : null + }); + + if (axisModel.get('triggerEvent')) { + textEl.eventData = makeAxisEventDataBase(axisModel); + textEl.eventData.targetType = 'axisName'; + textEl.eventData.name = name; + } + + // FIXME + this._dumbGroup.add(textEl); + textEl.updateTransform(); + + this.group.add(textEl); + + textEl.decomposeTransform(); + } + + }; + + /** + * @inner + */ + function innerTextLayout(opt, textRotation, direction) { + var rotationDiff = remRadian(textRotation - opt.rotation); + var textAlign; + var verticalAlign; + + if (isRadianAroundZero(rotationDiff)) { // Label is parallel with axis line. + verticalAlign = direction > 0 ? 'top' : 'bottom'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI)) { // Label is inverse parallel with axis line. + verticalAlign = direction > 0 ? 'bottom' : 'top'; + textAlign = 'center'; + } + else { + verticalAlign = 'middle'; + + if (rotationDiff > 0 && rotationDiff < PI) { + textAlign = direction > 0 ? 'right' : 'left'; + } + else { + textAlign = direction > 0 ? 'left' : 'right'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + verticalAlign: verticalAlign + }; + } + + /** + * @inner + */ + function endTextLayout(opt, textPosition, textRotate, extent) { + var rotationDiff = remRadian(textRotate - opt.rotation); + var textAlign; + var verticalAlign; + var inverse = extent[0] > extent[1]; + var onLeft = (textPosition === 'start' && !inverse) + || (textPosition !== 'start' && inverse); + + if (isRadianAroundZero(rotationDiff - PI / 2)) { + verticalAlign = onLeft ? 'bottom' : 'top'; + textAlign = 'center'; + } + else if (isRadianAroundZero(rotationDiff - PI * 1.5)) { + verticalAlign = onLeft ? 'top' : 'bottom'; + textAlign = 'center'; + } + else { + verticalAlign = 'middle'; + if (rotationDiff < PI * 1.5 && rotationDiff > PI / 2) { + textAlign = onLeft ? 'left' : 'right'; + } + else { + textAlign = onLeft ? 'right' : 'left'; + } + } + + return { + rotation: rotationDiff, + textAlign: textAlign, + verticalAlign: verticalAlign + }; + } + + /** + * @inner + */ + function isSilent(axisModel) { + var tooltipOpt = axisModel.get('tooltip'); + return axisModel.get('silent') + // Consider mouse cursor, add these restrictions. + || !( + axisModel.get('triggerEvent') || (tooltipOpt && tooltipOpt.show) + ); + } + + /** + * @static + */ + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick = function (axis, i, interval) { + var rawTick; + var scale = axis.scale; + return scale.type === 'ordinal' + && ( + typeof interval === 'function' + ? ( + rawTick = scale.getTicks()[i], + !interval(rawTick, scale.getLabel(rawTick)) + ) + : i % (interval + 1) + ); + }; + + /** + * @static + */ + var getInterval = AxisBuilder.getInterval = function (model, labelInterval) { + var interval = model.get('interval'); + if (interval == null || interval == 'auto') { + interval = labelInterval; + } + return interval; + }; + + module.exports = AxisBuilder; + + + +/***/ }, +/* 133 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + __webpack_require__(113); + + __webpack_require__(134); + __webpack_require__(135); + + var barLayoutGrid = __webpack_require__(137); + var echarts = __webpack_require__(1); + + echarts.registerLayout(zrUtil.curry(barLayoutGrid, 'bar')); + // Visual coding for legend + echarts.registerVisual(function (ecModel) { + ecModel.eachSeriesByType('bar', function (seriesModel) { + var data = seriesModel.getData(); + data.setVisual('legendSymbol', 'roundRect'); + }); + }); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 134 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var createListFromArray = __webpack_require__(101); + + module.exports = SeriesModel.extend({ + + type: 'series.bar', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + if (true) { + var coordSys = option.coordinateSystem; + if (coordSys !== 'cartesian2d') { + throw new Error('Bar only support cartesian2d coordinateSystem'); + } + } + return createListFromArray(option.data, this, ecModel); + }, + + getMarkerPosition: function (value) { + var coordSys = this.coordinateSystem; + if (coordSys) { + // PENDING if clamp ? + var pt = coordSys.dataToPoint(value, true); + var data = this.getData(); + var offset = data.getLayout('offset'); + var size = data.getLayout('size'); + var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1; + pt[offsetIndex] += offset + size / 2; + return pt; + } + return [NaN, NaN]; + }, + + brushSelector: 'rect', + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + // stack: null + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // 最小高度改为0 + barMinHeight: 0, + + // barMaxWidth: null, + // 默认自适应 + // barWidth: null, + // 柱间距离,默认为柱形宽度的30%,可设固定值 + // barGap: '30%', + // 类目间柱形距离,默认为类目间距的20%,可设固定值 + // barCategoryGap: '20%', + // label: { + // normal: { + // show: false + // } + // }, + itemStyle: { + normal: { + // color: '各异' + }, + emphasis: {} + } + } + }); + + +/***/ }, +/* 135 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + zrUtil.extend(__webpack_require__(12).prototype, __webpack_require__(136)); + + function fixLayoutWithLineWidth(layout, lineWidth) { + var signX = layout.width > 0 ? 1 : -1; + var signY = layout.height > 0 ? 1 : -1; + // In case width or height are too small. + lineWidth = Math.min(lineWidth, Math.abs(layout.width), Math.abs(layout.height)); + layout.x += signX * lineWidth / 2; + layout.y += signY * lineWidth / 2; + layout.width -= signX * lineWidth; + layout.height -= signY * lineWidth; + } + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'bar', + + render: function (seriesModel, ecModel, api) { + var coordinateSystemType = seriesModel.get('coordinateSystem'); + + if (coordinateSystemType === 'cartesian2d') { + this._renderOnCartesian(seriesModel, ecModel, api); + } + + return this.group; + }, + + _renderOnCartesian: function (seriesModel, ecModel, api) { + var group = this.group; + var data = seriesModel.getData(); + var oldData = this._data; + + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + var isHorizontal = baseAxis.isHorizontal(); + + var enableAnimation = seriesModel.get('animation'); + + var barBorderWidthQuery = ['itemStyle', 'normal', 'barBorderWidth']; + + function createRect(dataIndex, isUpdate) { + var layout = data.getItemLayout(dataIndex); + var lineWidth = data.getItemModel(dataIndex).get(barBorderWidthQuery) || 0; + fixLayoutWithLineWidth(layout, lineWidth); + + var rect = new graphic.Rect({ + shape: zrUtil.extend({}, layout) + }); + // Animation + if (enableAnimation) { + var rectShape = rect.shape; + var animateProperty = isHorizontal ? 'height' : 'width'; + var animateTarget = {}; + rectShape[animateProperty] = 0; + animateTarget[animateProperty] = layout[animateProperty]; + graphic[isUpdate? 'updateProps' : 'initProps'](rect, { + shape: animateTarget + }, seriesModel, dataIndex); + } + return rect; + } + data.diff(oldData) + .add(function (dataIndex) { + // 空数据 + if (!data.hasValue(dataIndex)) { + return; + } + + var rect = createRect(dataIndex); + + data.setItemGraphicEl(dataIndex, rect); + + group.add(rect); + + }) + .update(function (newIndex, oldIndex) { + var rect = oldData.getItemGraphicEl(oldIndex); + // 空数据 + if (!data.hasValue(newIndex)) { + group.remove(rect); + return; + } + if (!rect) { + rect = createRect(newIndex, true); + } + + var layout = data.getItemLayout(newIndex); + var lineWidth = data.getItemModel(newIndex).get(barBorderWidthQuery) || 0; + fixLayoutWithLineWidth(layout, lineWidth); + + graphic.updateProps(rect, { + shape: layout + }, seriesModel, newIndex); + + data.setItemGraphicEl(newIndex, rect); + + // Add back + group.add(rect); + }) + .remove(function (idx) { + var rect = oldData.getItemGraphicEl(idx); + if (rect) { + // Not show text when animating + rect.style.text = ''; + graphic.updateProps(rect, { + shape: { + width: 0 + } + }, seriesModel, idx, function () { + group.remove(rect); + }); + } + }) + .execute(); + + this._updateStyle(seriesModel, data, isHorizontal); + + this._data = data; + }, + + _updateStyle: function (seriesModel, data, isHorizontal) { + function setLabel(style, model, color, labelText, labelPositionOutside) { + graphic.setText(style, model, color); + style.text = labelText; + if (style.textPosition === 'outside') { + style.textPosition = labelPositionOutside; + } + } + + data.eachItemGraphicEl(function (rect, idx) { + var itemModel = data.getItemModel(idx); + var color = data.getItemVisual(idx, 'color'); + var opacity = data.getItemVisual(idx, 'opacity'); + var layout = data.getItemLayout(idx); + var itemStyleModel = itemModel.getModel('itemStyle.normal'); + + var hoverStyle = itemModel.getModel('itemStyle.emphasis').getBarItemStyle(); + + rect.setShape('r', itemStyleModel.get('barBorderRadius') || 0); + + rect.useStyle(zrUtil.defaults( + { + fill: color, + opacity: opacity + }, + itemStyleModel.getBarItemStyle() + )); + + var labelPositionOutside = isHorizontal + ? (layout.height > 0 ? 'bottom' : 'top') + : (layout.width > 0 ? 'left' : 'right'); + + var labelModel = itemModel.getModel('label.normal'); + var hoverLabelModel = itemModel.getModel('label.emphasis'); + var rectStyle = rect.style; + if (labelModel.get('show')) { + setLabel( + rectStyle, labelModel, color, + zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal'), + seriesModel.getRawValue(idx) + ), + labelPositionOutside + ); + } + else { + rectStyle.text = ''; + } + if (hoverLabelModel.get('show')) { + setLabel( + hoverStyle, hoverLabelModel, color, + zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis'), + seriesModel.getRawValue(idx) + ), + labelPositionOutside + ); + } + else { + hoverStyle.text = ''; + } + graphic.setHoverStyle(rect, hoverStyle); + }); + }, + + remove: function (ecModel, api) { + var group = this.group; + if (ecModel.get('animation')) { + if (this._data) { + this._data.eachItemGraphicEl(function (el) { + // Not show text when animating + el.style.text = ''; + graphic.updateProps(el, { + shape: { + width: 0 + } + }, ecModel, el.dataIndex, function () { + group.remove(el); + }); + }); + } + } + else { + group.removeAll(); + } + } + }); + + +/***/ }, +/* 136 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var getBarItemStyle = __webpack_require__(15)( + [ + ['fill', 'color'], + ['stroke', 'borderColor'], + ['lineWidth', 'borderWidth'], + // Compatitable with 2 + ['stroke', 'barBorderColor'], + ['lineWidth', 'barBorderWidth'], + ['opacity'], + ['shadowBlur'], + ['shadowOffsetX'], + ['shadowOffsetY'], + ['shadowColor'] + ] + ); + module.exports = { + getBarItemStyle: function (excludes) { + var style = getBarItemStyle.call(this, excludes); + if (this.getBorderLineDash) { + var lineDash = this.getBorderLineDash(); + lineDash && (style.lineDash = lineDash); + } + return style; + } + }; + + +/***/ }, +/* 137 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + + function getSeriesStackId(seriesModel) { + return seriesModel.get('stack') || '__ec_stack_' + seriesModel.seriesIndex; + } + + function getAxisKey(axis) { + return axis.dim + axis.index; + } + + function calBarWidthAndOffset(barSeries, api) { + // Columns info on each category axis. Key is cartesian name + var columnsMap = {}; + + zrUtil.each(barSeries, function (seriesModel, idx) { + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + + var baseAxis = cartesian.getBaseAxis(); + var axisExtent = baseAxis.getExtent(); + var bandWidth = baseAxis.type === 'category' + ? baseAxis.getBandWidth() + : (Math.abs(axisExtent[1] - axisExtent[0]) / data.count()); + + var columnsOnAxis = columnsMap[getAxisKey(baseAxis)] || { + bandWidth: bandWidth, + remainedWidth: bandWidth, + autoWidthCount: 0, + categoryGap: '20%', + gap: '30%', + stacks: {} + }; + var stacks = columnsOnAxis.stacks; + columnsMap[getAxisKey(baseAxis)] = columnsOnAxis; + + var stackId = getSeriesStackId(seriesModel); + + if (!stacks[stackId]) { + columnsOnAxis.autoWidthCount++; + } + stacks[stackId] = stacks[stackId] || { + width: 0, + maxWidth: 0 + }; + + var barWidth = parsePercent( + seriesModel.get('barWidth'), bandWidth + ); + var barMaxWidth = parsePercent( + seriesModel.get('barMaxWidth'), bandWidth + ); + var barGap = seriesModel.get('barGap'); + var barCategoryGap = seriesModel.get('barCategoryGap'); + // TODO + if (barWidth && !stacks[stackId].width) { + barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); + stacks[stackId].width = barWidth; + columnsOnAxis.remainedWidth -= barWidth; + } + + barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); + (barGap != null) && (columnsOnAxis.gap = barGap); + (barCategoryGap != null) && (columnsOnAxis.categoryGap = barCategoryGap); + }); + + var result = {}; + + zrUtil.each(columnsMap, function (columnsOnAxis, coordSysName) { + + result[coordSysName] = {}; + + var stacks = columnsOnAxis.stacks; + var bandWidth = columnsOnAxis.bandWidth; + var categoryGap = parsePercent(columnsOnAxis.categoryGap, bandWidth); + var barGapPercent = parsePercent(columnsOnAxis.gap, 1); + + var remainedWidth = columnsOnAxis.remainedWidth; + var autoWidthCount = columnsOnAxis.autoWidthCount; + var autoWidth = (remainedWidth - categoryGap) + / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + + // Find if any auto calculated bar exceeded maxBarWidth + zrUtil.each(stacks, function (column, stack) { + var maxWidth = column.maxWidth; + if (!column.width && maxWidth && maxWidth < autoWidth) { + maxWidth = Math.min(maxWidth, remainedWidth); + remainedWidth -= maxWidth; + column.width = maxWidth; + autoWidthCount--; + } + }); + + // Recalculate width again + autoWidth = (remainedWidth - categoryGap) + / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); + autoWidth = Math.max(autoWidth, 0); + + var widthSum = 0; + var lastColumn; + zrUtil.each(stacks, function (column, idx) { + if (!column.width) { + column.width = autoWidth; + } + lastColumn = column; + widthSum += column.width * (1 + barGapPercent); + }); + if (lastColumn) { + widthSum -= lastColumn.width * barGapPercent; + } + + var offset = -widthSum / 2; + zrUtil.each(stacks, function (column, stackId) { + result[coordSysName][stackId] = result[coordSysName][stackId] || { + offset: offset, + width: column.width + }; + + offset += column.width * (1 + barGapPercent); + }); + }); + + return result; + } + + /** + * @param {string} seriesType + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + function barLayoutGrid(seriesType, ecModel, api) { + + var barWidthAndOffset = calBarWidthAndOffset( + zrUtil.filter( + ecModel.getSeriesByType(seriesType), + function (seriesModel) { + return !ecModel.isSeriesFiltered(seriesModel) + && seriesModel.coordinateSystem + && seriesModel.coordinateSystem.type === 'cartesian2d'; + } + ) + ); + + var lastStackCoords = {}; + + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + + var data = seriesModel.getData(); + var cartesian = seriesModel.coordinateSystem; + var baseAxis = cartesian.getBaseAxis(); + + var stackId = getSeriesStackId(seriesModel); + var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; + var columnOffset = columnLayoutInfo.offset; + var columnWidth = columnLayoutInfo.width; + var valueAxis = cartesian.getOtherAxis(baseAxis); + + var barMinHeight = seriesModel.get('barMinHeight') || 0; + + var valueAxisStart = baseAxis.onZero + ? valueAxis.toGlobalCoord(valueAxis.dataToCoord(0)) + : valueAxis.getGlobalExtent()[0]; + + var coords = cartesian.dataToPoints(data, true); + lastStackCoords[stackId] = lastStackCoords[stackId] || []; + + data.setLayout({ + offset: columnOffset, + size: columnWidth + }); + data.each(valueAxis.dim, function (value, idx) { + // 空数据 + if (isNaN(value)) { + return; + } + if (!lastStackCoords[stackId][idx]) { + lastStackCoords[stackId][idx] = { + // Positive stack + p: valueAxisStart, + // Negative stack + n: valueAxisStart + }; + } + var sign = value >= 0 ? 'p' : 'n'; + var coord = coords[idx]; + var lastCoord = lastStackCoords[stackId][idx][sign]; + var x, y, width, height; + if (valueAxis.isHorizontal()) { + x = lastCoord; + y = coord[1] + columnOffset; + width = coord[0] - lastCoord; + height = columnWidth; + + if (Math.abs(width) < barMinHeight) { + width = (width < 0 ? -1 : 1) * barMinHeight; + } + lastStackCoords[stackId][idx][sign] += width; + } + else { + x = coord[0] + columnOffset; + y = lastCoord; + width = columnWidth; + height = coord[1] - lastCoord; + if (Math.abs(height) < barMinHeight) { + // Include zero to has a positive bar + height = (height <= 0 ? -1 : 1) * barMinHeight; + } + lastStackCoords[stackId][idx][sign] += height; + } + + data.setItemLayout(idx, { + x: x, + y: y, + width: width, + height: height + }); + }, true); + + }, this); + } + + module.exports = barLayoutGrid; + + +/***/ }, +/* 138 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(139); + __webpack_require__(141); + + __webpack_require__(142)('pie', [{ + type: 'pieToggleSelect', + event: 'pieselectchanged', + method: 'toggleSelected' + }, { + type: 'pieSelect', + event: 'pieselected', + method: 'select' + }, { + type: 'pieUnSelect', + event: 'pieunselected', + method: 'unSelect' + }]); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'pie')); + + echarts.registerLayout(zrUtil.curry( + __webpack_require__(144), 'pie' + )); + + echarts.registerProcessor(zrUtil.curry(__webpack_require__(146), 'pie')); + + +/***/ }, +/* 139 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var completeDimensions = __webpack_require__(102); + + var dataSelectableMixin = __webpack_require__(140); + + var PieSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.pie', + + // Overwrite + init: function (option) { + PieSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + + this.updateSelectedMap(option.data); + + this._defaultLabelLine(option); + }, + + // Overwrite + mergeOption: function (newOption) { + PieSeries.superCall(this, 'mergeOption', newOption); + this.updateSelectedMap(this.option.data); + }, + + getInitialData: function (option, ecModel) { + var dimensions = completeDimensions(['value'], option.data); + var list = new List(dimensions, this); + list.initData(option.data); + return list; + }, + + // Overwrite + getDataParams: function (dataIndex) { + var data = this._data; + var params = PieSeries.superCall(this, 'getDataParams', dataIndex); + var sum = data.getSum('value'); + // FIXME toFixed? + // + // Percent is 0 if sum is 0 + params.percent = !sum ? 0 : +(data.get('value', dataIndex) / sum * 100).toFixed(2); + + params.$vars.push('percent'); + return params; + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + modelUtil.defaultEmphasis(option.labelLine, ['show']); + + var labelLineNormalOpt = option.labelLine.normal; + var labelLineEmphasisOpt = option.labelLine.emphasis; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.normal.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.label.emphasis.show; + }, + + defaultOption: { + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // 默认全局居中 + center: ['50%', '50%'], + radius: [0, '75%'], + // 默认顺时针 + clockwise: true, + startAngle: 90, + // 最小角度改为0 + minAngle: 0, + // 选中是扇区偏移量 + selectedOffset: 10, + + // If use strategy to avoid label overlapping + avoidLabelOverlap: true, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积) + // roseType: null, + + label: { + normal: { + // If rotate around circle + rotate: false, + show: true, + // 'outer', 'inside', 'center' + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 + }, + emphasis: {} + }, + // Enabled when label.normal.position is 'outer' + labelLine: { + normal: { + show: true, + // 引导线两段中的第一段长度 + length: 15, + // 引导线两段中的第二段长度 + length2: 15, + smooth: false, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + } + }, + itemStyle: { + normal: { + borderWidth: 1 + }, + emphasis: {} + }, + + animationEasing: 'cubicOut', + + data: [] + } + }); + + zrUtil.mixin(PieSeries, dataSelectableMixin); + + module.exports = PieSeries; + + +/***/ }, +/* 140 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Data selectable mixin for chart series. + * To eanble data select, option of series must have `selectedMode`. + * And each data item will use `selected` to toggle itself selected status + * + * @module echarts/chart/helper/DataSelectable + */ + + + var zrUtil = __webpack_require__(4); + + module.exports = { + + updateSelectedMap: function (targetList) { + this._selectTargetMap = zrUtil.reduce(targetList || [], function (targetMap, target) { + targetMap[target.name] = target; + return targetMap; + }, {}); + }, + /** + * @param {string} name + */ + // PENGING If selectedMode is null ? + select: function (name) { + var targetMap = this._selectTargetMap; + var target = targetMap[name]; + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + zrUtil.each(targetMap, function (target) { + target.selected = false; + }); + } + target && (target.selected = true); + }, + + /** + * @param {string} name + */ + unSelect: function (name) { + var target = this._selectTargetMap[name]; + // var selectedMode = this.get('selectedMode'); + // selectedMode !== 'single' && target && (target.selected = false); + target && (target.selected = false); + }, + + /** + * @param {string} name + */ + toggleSelected: function (name) { + var target = this._selectTargetMap[name]; + if (target != null) { + this[target.selected ? 'unSelect' : 'select'](name); + return target.selected; + } + }, + + /** + * @param {string} name + */ + isSelected: function (name) { + var target = this._selectTargetMap[name]; + return target && target.selected; + } + }; + + +/***/ }, +/* 141 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * @param {module:echarts/model/Series} seriesModel + * @param {boolean} hasAnimation + * @inner + */ + function updateDataSelected(uid, seriesModel, hasAnimation, api) { + var data = seriesModel.getData(); + var dataIndex = this.dataIndex; + var name = data.getName(dataIndex); + var selectedOffset = seriesModel.get('selectedOffset'); + + api.dispatchAction({ + type: 'pieToggleSelect', + from: uid, + name: name, + seriesId: seriesModel.id + }); + + data.each(function (idx) { + toggleItemSelected( + data.getItemGraphicEl(idx), + data.getItemLayout(idx), + seriesModel.isSelected(data.getName(idx)), + selectedOffset, + hasAnimation + ); + }); + } + + /** + * @param {module:zrender/graphic/Sector} el + * @param {Object} layout + * @param {boolean} isSelected + * @param {number} selectedOffset + * @param {boolean} hasAnimation + * @inner + */ + function toggleItemSelected(el, layout, isSelected, selectedOffset, hasAnimation) { + var midAngle = (layout.startAngle + layout.endAngle) / 2; + + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + + var offset = isSelected ? selectedOffset : 0; + var position = [dx * offset, dy * offset]; + + hasAnimation + // animateTo will stop revious animation like update transition + ? el.animate() + .when(200, { + position: position + }) + .start('bounceOut') + : el.attr('position', position); + } + + /** + * Piece of pie including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ + function PiePiece(data, idx) { + + graphic.Group.call(this); + + var sector = new graphic.Sector({ + z2: 2 + }); + var polyline = new graphic.Polyline(); + var text = new graphic.Text(); + this.add(sector); + this.add(polyline); + this.add(text); + + this.updateData(data, idx, true); + + // Hover to change label and labelLine + function onEmphasis() { + polyline.ignore = polyline.hoverIgnore; + text.ignore = text.hoverIgnore; + } + function onNormal() { + polyline.ignore = polyline.normalIgnore; + text.ignore = text.normalIgnore; + } + this.on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('mouseover', onEmphasis) + .on('mouseout', onNormal); + } + + var piePieceProto = PiePiece.prototype; + + function getLabelStyle(data, idx, state, labelModel, labelPosition) { + var textStyleModel = labelModel.getModel('textStyle'); + var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; + return { + fill: textStyleModel.getTextColor() + || (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')), + opacity: data.getItemVisual(idx, 'opacity'), + textFont: textStyleModel.getFont(), + text: zrUtil.retrieve( + data.hostModel.getFormattedLabel(idx, state), data.getName(idx) + ) + }; + } + + piePieceProto.updateData = function (data, idx, firstCreate) { + + var sector = this.childAt(0); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var sectorShape = zrUtil.extend({}, layout); + sectorShape.label = null; + if (firstCreate) { + sector.setShape(sectorShape); + sector.shape.endAngle = layout.startAngle; + graphic.updateProps(sector, { + shape: { + endAngle: layout.endAngle + } + }, seriesModel, idx); + } + else { + graphic.updateProps(sector, { + shape: sectorShape + }, seriesModel, idx); + } + + // Update common style + var itemStyleModel = itemModel.getModel('itemStyle'); + var visualColor = data.getItemVisual(idx, 'color'); + + sector.useStyle( + zrUtil.defaults( + { + lineJoin: 'bevel', + fill: visualColor + }, + itemStyleModel.getModel('normal').getItemStyle() + ) + ); + sector.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); + + // Toggle selected + toggleItemSelected( + this, + data.getItemLayout(idx), + itemModel.get('selected'), + seriesModel.get('selectedOffset'), + seriesModel.get('animation') + ); + + function onEmphasis() { + // Sector may has animation of updating data. Force to move to the last frame + // Or it may stopped on the wrong shape + sector.stopAnimation(true); + sector.animateTo({ + shape: { + r: layout.r + 10 + } + }, 300, 'elasticOut'); + } + function onNormal() { + sector.stopAnimation(true); + sector.animateTo({ + shape: { + r: layout.r + } + }, 300, 'elasticOut'); + } + sector.off('mouseover').off('mouseout').off('emphasis').off('normal'); + if (itemModel.get('hoverAnimation') && seriesModel.ifEnableAnimation()) { + sector + .on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + } + + this._updateLabel(data, idx); + + graphic.setHoverStyle(this); + }; + + piePieceProto._updateLabel = function (data, idx) { + + var labelLine = this.childAt(1); + var labelText = this.childAt(2); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var labelLayout = layout.label; + var visualColor = data.getItemVisual(idx, 'color'); + + graphic.updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || [ + [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y], [labelLayout.x, labelLayout.y] + ] + } + }, seriesModel, idx); + + graphic.updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + style: { + textVerticalAlign: labelLayout.verticalAlign, + textAlign: labelLayout.textAlign, + textFont: labelLayout.font + }, + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineHoverModel = itemModel.getModel('labelLine.emphasis'); + var labelPosition = labelModel.get('position') || labelHoverModel.get('position'); + + labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel, labelPosition)); + + labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); + labelText.hoverIgnore = !labelHoverModel.get('show'); + + labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); + labelLine.hoverIgnore = !labelLineHoverModel.get('show'); + + // Default use item visual color + labelLine.setStyle({ + stroke: visualColor, + opacity: data.getItemVisual(idx, 'opacity') + }); + labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); + + labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel, labelPosition); + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); + + var smooth = labelLineModel.get('smooth'); + if (smooth && smooth === true) { + smooth = 0.4; + } + labelLine.setShape({ + smooth: smooth + }); + }; + + zrUtil.inherits(PiePiece, graphic.Group); + + + // Pie view + var Pie = __webpack_require__(42).extend({ + + type: 'pie', + + init: function () { + var sectorGroup = new graphic.Group(); + this._sectorGroup = sectorGroup; + }, + + render: function (seriesModel, ecModel, api, payload) { + if (payload && (payload.from === this.uid)) { + return; + } + + var data = seriesModel.getData(); + var oldData = this._data; + var group = this.group; + + var hasAnimation = ecModel.get('animation'); + var isFirstRender = !oldData; + + var onSectorClick = zrUtil.curry( + updateDataSelected, this.uid, seriesModel, hasAnimation, api + ); + + var selectedMode = seriesModel.get('selectedMode'); + + data.diff(oldData) + .add(function (idx) { + var piePiece = new PiePiece(data, idx); + if (isFirstRender) { + piePiece.eachChild(function (child) { + child.stopAnimation(true); + }); + } + + selectedMode && piePiece.on('click', onSectorClick); + + data.setItemGraphicEl(idx, piePiece); + + group.add(piePiece); + }) + .update(function (newIdx, oldIdx) { + var piePiece = oldData.getItemGraphicEl(oldIdx); + + piePiece.updateData(data, newIdx); + + piePiece.off('click'); + selectedMode && piePiece.on('click', onSectorClick); + group.add(piePiece); + data.setItemGraphicEl(newIdx, piePiece); + }) + .remove(function (idx) { + var piePiece = oldData.getItemGraphicEl(idx); + group.remove(piePiece); + }) + .execute(); + + if (hasAnimation && isFirstRender && data.count() > 0) { + var shape = data.getItemLayout(0); + var r = Math.max(api.getWidth(), api.getHeight()) / 2; + + var removeClipPath = zrUtil.bind(group.removeClipPath, group); + group.setClipPath(this._createClipPath( + shape.cx, shape.cy, r, shape.startAngle, shape.clockwise, removeClipPath, seriesModel + )); + } + + this._data = data; + }, + + _createClipPath: function ( + cx, cy, r, startAngle, clockwise, cb, seriesModel + ) { + var clipPath = new graphic.Sector({ + shape: { + cx: cx, + cy: cy, + r0: 0, + r: r, + startAngle: startAngle, + endAngle: startAngle, + clockwise: clockwise + } + }); + + graphic.initProps(clipPath, { + shape: { + endAngle: startAngle + (clockwise ? 1 : -1) * Math.PI * 2 + } + }, seriesModel, cb); + + return clipPath; + } + }); + + module.exports = Pie; + + +/***/ }, +/* 142 */ +/***/ function(module, exports, __webpack_require__) { + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + module.exports = function (seriesType, actionInfos) { + zrUtil.each(actionInfos, function (actionInfo) { + actionInfo.update = 'updateView'; + /** + * @payload + * @property {string} seriesName + * @property {string} name + */ + echarts.registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + ecModel.eachComponent( + {mainType: 'series', subType: seriesType, query: payload}, + function (seriesModel) { + if (seriesModel[actionInfo.method]) { + seriesModel[actionInfo.method](payload.name); + } + var data = seriesModel.getData(); + // Create selected map + data.each(function (idx) { + var name = data.getName(idx); + selected[name] = seriesModel.isSelected(name) || false; + }); + } + ); + return { + name: payload.name, + selected: selected + }; + }); + }); + }; + + +/***/ }, +/* 143 */ +/***/ function(module, exports) { + + // Pick color from palette for each data item + + + module.exports = function (seriesType, ecModel) { + // Pie and funnel may use diferrent scope + var paletteScope = {}; + ecModel.eachRawSeriesByType(seriesType, function (seriesModel) { + var dataAll = seriesModel.getRawData(); + var idxMap = {}; + if (!ecModel.isSeriesFiltered(seriesModel)) { + var data = seriesModel.getData(); + data.each(function (idx) { + var rawIdx = data.getRawIndex(idx); + idxMap[rawIdx] = idx; + }); + dataAll.each(function (rawIdx) { + // FIXME Performance + var itemModel = dataAll.getItemModel(rawIdx); + var filteredIdx = idxMap[rawIdx]; + + // If series.itemStyle.normal.color is a function. itemVisual may be encoded + var singleDataColor = filteredIdx != null + && data.getItemVisual(filteredIdx, 'color', true); + + if (!singleDataColor) { + var color = itemModel.get('itemStyle.normal.color') + || seriesModel.getColorFromPalette(dataAll.getName(rawIdx), paletteScope); + // Legend may use the visual info in data before processed + dataAll.setItemVisual(rawIdx, 'color', color); + + // Data is not filtered + if (filteredIdx != null) { + data.setItemVisual(filteredIdx, 'color', color); + } + } + else { + // Set data all color for legend + dataAll.setItemVisual(rawIdx, 'color', singleDataColor); + } + }); + } + }); + }; + + +/***/ }, +/* 144 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO minAngle + + + + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var labelLayout = __webpack_require__(145); + var zrUtil = __webpack_require__(4); + + var PI2 = Math.PI * 2; + var RADIAN = Math.PI / 180; + + module.exports = function (seriesType, ecModel, api, payload) { + ecModel.eachSeriesByType(seriesType, function (seriesModel) { + var center = seriesModel.get('center'); + var radius = seriesModel.get('radius'); + + if (!zrUtil.isArray(radius)) { + radius = [0, radius]; + } + if (!zrUtil.isArray(center)) { + center = [center, center]; + } + + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent(center[0], width); + var cy = parsePercent(center[1], height); + var r0 = parsePercent(radius[0], size / 2); + var r = parsePercent(radius[1], size / 2); + + var data = seriesModel.getData(); + + var startAngle = -seriesModel.get('startAngle') * RADIAN; + + var minAngle = seriesModel.get('minAngle') * RADIAN; + + var sum = data.getSum('value'); + // Sum may be 0 + var unitRadian = Math.PI / (sum || data.count()) * 2; + + var clockwise = seriesModel.get('clockwise'); + + var roseType = seriesModel.get('roseType'); + + // [0...max] + var extent = data.getDataExtent('value'); + extent[0] = 0; + + // In the case some sector angle is smaller than minAngle + var restAngle = PI2; + var valueSumLargerThanMinAngle = 0; + + var currentAngle = startAngle; + + var dir = clockwise ? 1 : -1; + data.each('value', function (value, idx) { + var angle; + // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? + if (roseType !== 'area') { + angle = sum === 0 ? unitRadian : (value * unitRadian); + } + else { + angle = PI2 / (data.count() || 1); + } + + if (angle < minAngle) { + angle = minAngle; + restAngle -= minAngle; + } + else { + valueSumLargerThanMinAngle += value; + } + + var endAngle = currentAngle + dir * angle; + data.setItemLayout(idx, { + angle: angle, + startAngle: currentAngle, + endAngle: endAngle, + clockwise: clockwise, + cx: cx, + cy: cy, + r0: r0, + r: roseType + ? numberUtil.linearMap(value, extent, [r0, r]) + : r + }); + + currentAngle = endAngle; + }, true); + + // Some sector is constrained by minAngle + // Rest sectors needs recalculate angle + if (restAngle < PI2) { + // Average the angle if rest angle is not enough after all angles is + // Constrained by minAngle + if (restAngle <= 1e-3) { + var angle = PI2 / data.count(); + data.each(function (idx) { + var layout = data.getItemLayout(idx); + layout.startAngle = startAngle + dir * idx * angle; + layout.endAngle = startAngle + dir * (idx + 1) * angle; + }); + } + else { + unitRadian = restAngle / valueSumLargerThanMinAngle; + currentAngle = startAngle; + data.each('value', function (value, idx) { + var layout = data.getItemLayout(idx); + var angle = layout.angle === minAngle + ? minAngle : value * unitRadian; + layout.startAngle = currentAngle; + layout.endAngle = currentAngle + dir * angle; + currentAngle += angle; + }); + } + } + + labelLayout(seriesModel, r, width, height); + }); + }; + + +/***/ }, +/* 145 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + // FIXME emphasis label position is not same with normal label position + + + var textContain = __webpack_require__(8); + + function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight) { + list.sort(function (a, b) { + return a.y - b.y; + }); + + // 压 + function shiftDown(start, end, delta, dir) { + for (var j = start; j < end; j++) { + list[j].y += delta; + if (j > start + && j + 1 < end + && list[j + 1].y > list[j].y + list[j].height + ) { + shiftUp(j, delta / 2); + return; + } + } + + shiftUp(end - 1, delta / 2); + } + + // 弹 + function shiftUp(end, delta) { + for (var j = end; j >= 0; j--) { + list[j].y -= delta; + if (j > 0 + && list[j].y > list[j - 1].y + list[j - 1].height + ) { + break; + } + } + } + + function changeX(list, isDownList, cx, cy, r, dir) { + var lastDeltaX = dir > 0 + ? isDownList // 右侧 + ? Number.MAX_VALUE // 下 + : 0 // 上 + : isDownList // 左侧 + ? Number.MAX_VALUE // 下 + : 0; // 上 + + for (var i = 0, l = list.length; i < l; i++) { + // Not change x for center label + if (list[i].position === 'center') { + continue; + } + var deltaY = Math.abs(list[i].y - cy); + var length = list[i].len; + var length2 = list[i].len2; + var deltaX = (deltaY < r + length) + ? Math.sqrt( + (r + length + length2) * (r + length + length2) + - deltaY * deltaY + ) + : Math.abs(list[i].x - cx); + if (isDownList && deltaX >= lastDeltaX) { + // 右下,左下 + deltaX = lastDeltaX - 10; + } + if (!isDownList && deltaX <= lastDeltaX) { + // 右上,左上 + deltaX = lastDeltaX + 10; + } + + list[i].x = cx + deltaX * dir; + lastDeltaX = deltaX; + } + } + + var lastY = 0; + var delta; + var len = list.length; + var upList = []; + var downList = []; + for (var i = 0; i < len; i++) { + delta = list[i].y - lastY; + if (delta < 0) { + shiftDown(i, len, -delta, dir); + } + lastY = list[i].y + list[i].height; + } + if (viewHeight - lastY < 0) { + shiftUp(len - 1, lastY - viewHeight); + } + for (var i = 0; i < len; i++) { + if (list[i].y >= cy) { + downList.push(list[i]); + } + else { + upList.push(list[i]); + } + } + changeX(upList, false, cx, cy, r, dir); + changeX(downList, true, cx, cy, r, dir); + } + + function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight) { + var leftList = []; + var rightList = []; + for (var i = 0; i < labelLayoutList.length; i++) { + if (labelLayoutList[i].x < cx) { + leftList.push(labelLayoutList[i]); + } + else { + rightList.push(labelLayoutList[i]); + } + } + + adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight); + adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight); + + for (var i = 0; i < labelLayoutList.length; i++) { + var linePoints = labelLayoutList[i].linePoints; + if (linePoints) { + var dist = linePoints[1][0] - linePoints[2][0]; + if (labelLayoutList[i].x < cx) { + linePoints[2][0] = labelLayoutList[i].x + 3; + } + else { + linePoints[2][0] = labelLayoutList[i].x - 3; + } + linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y; + linePoints[1][0] = linePoints[2][0] + dist; + } + } + } + + module.exports = function (seriesModel, r, viewWidth, viewHeight) { + var data = seriesModel.getData(); + var labelLayoutList = []; + var cx; + var cy; + var hasLabelRotate = false; + + data.each(function (idx) { + var layout = data.getItemLayout(idx); + + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + // Use position in normal or emphasis + var labelPosition = labelModel.get('position') || itemModel.get('label.emphasis.position'); + + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineLen = labelLineModel.get('length'); + var labelLineLen2 = labelLineModel.get('length2'); + + var midAngle = (layout.startAngle + layout.endAngle) / 2; + var dx = Math.cos(midAngle); + var dy = Math.sin(midAngle); + + var textX; + var textY; + var linePoints; + var textAlign; + + cx = layout.cx; + cy = layout.cy; + + var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; + if (labelPosition === 'center') { + textX = layout.cx; + textY = layout.cy; + textAlign = 'center'; + } + else { + var x1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dx : layout.r * dx) + cx; + var y1 = (isLabelInside ? (layout.r + layout.r0) / 2 * dy : layout.r * dy) + cy; + + textX = x1 + dx * 3; + textY = y1 + dy * 3; + + if (!isLabelInside) { + // For roseType + var x2 = x1 + dx * (labelLineLen + r - layout.r); + var y2 = y1 + dy * (labelLineLen + r - layout.r); + var x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2); + var y3 = y2; + + textX = x3 + (dx < 0 ? -5 : 5); + textY = y3; + linePoints = [[x1, y1], [x2, y2], [x3, y3]]; + } + + textAlign = isLabelInside ? 'center' : (dx > 0 ? 'left' : 'right'); + } + var font = labelModel.getModel('textStyle').getFont(); + + var labelRotate = labelModel.get('rotate') + ? (dx < 0 ? -midAngle + Math.PI : -midAngle) : 0; + var text = seriesModel.getFormattedLabel(idx, 'normal') + || data.getName(idx); + var textRect = textContain.getBoundingRect( + text, font, textAlign, 'top' + ); + hasLabelRotate = !!labelRotate; + layout.label = { + x: textX, + y: textY, + position: labelPosition, + height: textRect.height, + len: labelLineLen, + len2: labelLineLen2, + linePoints: linePoints, + textAlign: textAlign, + verticalAlign: 'middle', + font: font, + rotation: labelRotate + }; + + // Not layout the inside label + if (!isLabelInside) { + labelLayoutList.push(layout.label); + } + }); + if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { + avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight); + } + }; + + +/***/ }, +/* 146 */ +/***/ function(module, exports) { + + + module.exports = function (seriesType, ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + ecModel.eachSeriesByType(seriesType, function (series) { + var data = series.getData(); + data.filterSelf(function (idx) { + var name = data.getName(idx); + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(name)) { + return false; + } + } + return true; + }, this); + }, this); + }; + + +/***/ }, +/* 147 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(148); + __webpack_require__(149); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'scatter', 'circle', null + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'scatter' + )); + + // In case developer forget to include grid component + __webpack_require__(112); + + +/***/ }, +/* 148 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.scatter', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + var list = createListFromArray(option.data, this, ecModel); + return list; + }, + + brushSelector: 'point', + + defaultOption: { + coordinateSystem: 'cartesian2d', + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Polar coordinate system + // polarIndex: 0, + + // Geo coordinate system + // geoIndex: 0, + + // symbol: null, // 图形类型 + symbolSize: 10, // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2 + // symbolRotate: null, // 图形旋转控制 + + large: false, + // Available when large is true + largeThreshold: 2000, + + // label: { + // normal: { + // show: false + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // position: 默认自适应,水平布局为'top',垂直布局为'right',可选为 + // 'inside'|'left'|'right'|'top'|'bottom' + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + // } + // }, + itemStyle: { + normal: { + opacity: 0.8 + // color: 各异 + } + } + } + + }); + + +/***/ }, +/* 149 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var LargeSymbolDraw = __webpack_require__(150); + + __webpack_require__(1).extendChartView({ + + type: 'scatter', + + init: function () { + this._normalSymbolDraw = new SymbolDraw(); + this._largeSymbolDraw = new LargeSymbolDraw(); + }, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var largeSymbolDraw = this._largeSymbolDraw; + var normalSymbolDraw = this._normalSymbolDraw; + var group = this.group; + + var symbolDraw = seriesModel.get('large') && data.count() > seriesModel.get('largeThreshold') + ? largeSymbolDraw : normalSymbolDraw; + + this._symbolDraw = symbolDraw; + symbolDraw.updateData(data); + group.add(symbolDraw.group); + + group.remove( + symbolDraw === largeSymbolDraw + ? normalSymbolDraw.group : largeSymbolDraw.group + ); + }, + + updateLayout: function (seriesModel) { + this._symbolDraw.updateLayout(seriesModel); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(api, true); + } + }); + + +/***/ }, +/* 150 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Batch by color + + + + var graphic = __webpack_require__(43); + var symbolUtil = __webpack_require__(106); + + var LargeSymbolPath = graphic.extendShape({ + + shape: { + points: null, + sizes: null + }, + + symbolProxy: null, + + buildPath: function (path, shape) { + var points = shape.points; + var sizes = shape.sizes; + + var symbolProxy = this.symbolProxy; + var symbolProxyShape = symbolProxy.shape; + for (var i = 0; i < points.length; i++) { + var pt = points[i]; + var size = sizes[i]; + if (size[0] < 4) { + // Optimize for small symbol + path.rect( + pt[0] - size[0] / 2, pt[1] - size[1] / 2, + size[0], size[1] + ); + } + else { + symbolProxyShape.x = pt[0] - size[0] / 2; + symbolProxyShape.y = pt[1] - size[1] / 2; + symbolProxyShape.width = size[0]; + symbolProxyShape.height = size[1]; + + symbolProxy.buildPath(path, symbolProxyShape, true); + } + } + }, + + findDataIndex: function (x, y) { + var shape = this.shape; + var points = shape.points; + var sizes = shape.sizes; + + // Not consider transform + // Treat each element as a rect + // top down traverse + for (var i = points.length - 1; i >= 0; i--) { + var pt = points[i]; + var size = sizes[i]; + var x0 = pt[0] - size[0] / 2; + var y0 = pt[1] - size[1] / 2; + if (x >= x0 && y >= y0 && x <= x0 + size[0] && y <= y0 + size[1]) { + // i is dataIndex + return i; + } + } + + return -1; + } + }); + + function LargeSymbolDraw() { + this.group = new graphic.Group(); + + this._symbolEl = new LargeSymbolPath({ + // rectHover: true, + // cursor: 'default' + }); + } + + var largeSymbolProto = LargeSymbolDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + largeSymbolProto.updateData = function (data) { + this.group.removeAll(); + + var symbolEl = this._symbolEl; + + var seriesModel = data.hostModel; + + symbolEl.setShape({ + points: data.mapArray(data.getItemLayout), + sizes: data.mapArray( + function (idx) { + var size = data.getItemVisual(idx, 'symbolSize'); + if (!(size instanceof Array)) { + size = [size, size]; + } + return size; + } + ) + }); + + // Create symbolProxy to build path for each data + symbolEl.symbolProxy = symbolUtil.createSymbol( + data.getVisual('symbol'), 0, 0, 0, 0 + ); + // Use symbolProxy setColor method + symbolEl.setColor = symbolEl.symbolProxy.setColor; + + symbolEl.useStyle( + seriesModel.getModel('itemStyle.normal').getItemStyle(['color']) + ); + + var visualColor = data.getVisual('color'); + if (visualColor) { + symbolEl.setColor(visualColor); + } + + // Enable tooltip + // PENDING May have performance issue when path is extremely large + symbolEl.seriesIndex = seriesModel.seriesIndex; + symbolEl.on('mousemove', function (e) { + symbolEl.dataIndex = null; + var dataIndex = symbolEl.findDataIndex(e.offsetX, e.offsetY); + if (dataIndex > 0) { + // Provide dataIndex for tooltip + symbolEl.dataIndex = dataIndex; + } + }); + + // Add back + this.group.add(symbolEl); + }; + + largeSymbolProto.updateLayout = function (seriesModel) { + var data = seriesModel.getData(); + this._symbolEl.setShape({ + points: data.mapArray(data.getItemLayout) + }); + }; + + largeSymbolProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LargeSymbolDraw; + + +/***/ }, +/* 151 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + // Must use radar component + __webpack_require__(152); + + __webpack_require__(157); + __webpack_require__(158); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'radar')); + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'radar', 'circle', null + )); + echarts.registerLayout(__webpack_require__(159)); + + echarts.registerProcessor( + zrUtil.curry(__webpack_require__(146), 'radar') + ); + + echarts.registerPreprocessor(__webpack_require__(160)); + + +/***/ }, +/* 152 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(153); + __webpack_require__(155); + + __webpack_require__(156); + + +/***/ }, +/* 153 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO clockwise + + + var zrUtil = __webpack_require__(4); + var IndicatorAxis = __webpack_require__(154); + var IntervalScale = __webpack_require__(117); + var numberUtil = __webpack_require__(7); + var axisHelper = __webpack_require__(114); + + function Radar(radarModel, ecModel, api) { + + this._model = radarModel; + /** + * Radar dimensions + * @type {Array.} + */ + this.dimensions = []; + + this._indicatorAxes = zrUtil.map(radarModel.getIndicatorModels(), function (indicatorModel, idx) { + var dim = 'indicator_' + idx; + var indicatorAxis = new IndicatorAxis(dim, new IntervalScale()); + indicatorAxis.name = indicatorModel.get('name'); + // Inject model and axis + indicatorAxis.model = indicatorModel; + indicatorModel.axis = indicatorAxis; + this.dimensions.push(dim); + return indicatorAxis; + }, this); + + this.resize(radarModel, api); + + /** + * @type {number} + * @readOnly + */ + this.cx; + /** + * @type {number} + * @readOnly + */ + this.cy; + /** + * @type {number} + * @readOnly + */ + this.r; + /** + * @type {number} + * @readOnly + */ + this.startAngle; + } + + Radar.prototype.getIndicatorAxes = function () { + return this._indicatorAxes; + }; + + Radar.prototype.dataToPoint = function (value, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + + return this.coordToPoint(indicatorAxis.dataToCoord(value), indicatorIndex); + }; + + Radar.prototype.coordToPoint = function (coord, indicatorIndex) { + var indicatorAxis = this._indicatorAxes[indicatorIndex]; + var angle = indicatorAxis.angle; + var x = this.cx + coord * Math.cos(angle); + var y = this.cy - coord * Math.sin(angle); + return [x, y]; + }; + + Radar.prototype.pointToData = function (pt) { + var dx = pt[0] - this.cx; + var dy = pt[1] - this.cy; + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + + var radian = Math.atan2(-dy, dx); + + // Find the closest angle + // FIXME index can calculated directly + var minRadianDiff = Infinity; + var closestAxis; + var closestAxisIdx = -1; + for (var i = 0; i < this._indicatorAxes.length; i++) { + var indicatorAxis = this._indicatorAxes[i]; + var diff = Math.abs(radian - indicatorAxis.angle); + if (diff < minRadianDiff) { + closestAxis = indicatorAxis; + closestAxisIdx = i; + minRadianDiff = diff; + } + } + + return [closestAxisIdx, +(closestAxis && closestAxis.coodToData(radius))]; + }; + + Radar.prototype.resize = function (radarModel, api) { + var center = radarModel.get('center'); + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + var viewSize = Math.min(viewWidth, viewHeight) / 2; + this.cx = numberUtil.parsePercent(center[0], viewWidth); + this.cy = numberUtil.parsePercent(center[1], viewHeight); + + this.startAngle = radarModel.get('startAngle') * Math.PI / 180; + + this.r = numberUtil.parsePercent(radarModel.get('radius'), viewSize); + + zrUtil.each(this._indicatorAxes, function (indicatorAxis, idx) { + indicatorAxis.setExtent(0, this.r); + var angle = (this.startAngle + idx * Math.PI * 2 / this._indicatorAxes.length); + // Normalize to [-PI, PI] + angle = Math.atan2(Math.sin(angle), Math.cos(angle)); + indicatorAxis.angle = angle; + }, this); + }; + + Radar.prototype.update = function (ecModel, api) { + var indicatorAxes = this._indicatorAxes; + var radarModel = this._model; + zrUtil.each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.setExtent(Infinity, -Infinity); + }); + ecModel.eachSeriesByType('radar', function (radarSeries, idx) { + if (radarSeries.get('coordinateSystem') !== 'radar' + || ecModel.getComponent('radar', radarSeries.get('radarIndex')) !== radarModel + ) { + return; + } + var data = radarSeries.getData(); + zrUtil.each(indicatorAxes, function (indicatorAxis) { + indicatorAxis.scale.unionExtent(data.getDataExtent(indicatorAxis.dim)); + }); + }, this); + + var splitNumber = radarModel.get('splitNumber'); + + function increaseInterval(interval) { + var exp10 = Math.pow(10, Math.floor(Math.log(interval) / Math.LN10)); + // Increase interval + var f = interval / exp10; + if (f === 2) { + f = 5; + } + else { // f is 2 or 5 + f *= 2; + } + return f * exp10; + } + // Force all the axis fixing the maxSplitNumber. + zrUtil.each(indicatorAxes, function (indicatorAxis, idx) { + var rawExtent = axisHelper.getScaleExtent(indicatorAxis, indicatorAxis.model); + axisHelper.niceScaleExtent(indicatorAxis, indicatorAxis.model); + + var axisModel = indicatorAxis.model; + var scale = indicatorAxis.scale; + var fixedMin = axisModel.get('min'); + var fixedMax = axisModel.get('max'); + var interval = scale.getInterval(); + + if (fixedMin != null && fixedMax != null) { + // User set min, max, divide to get new interval + // FIXME precision + scale.setInterval( + (fixedMax - fixedMin) / splitNumber + ); + } + else if (fixedMin != null) { + var max; + // User set min, expand extent on the other side + do { + max = fixedMin + interval * splitNumber; + scale.setExtent(+fixedMin, max); + // Interval must been set after extent + // FIXME + scale.setInterval(interval); + + interval = increaseInterval(interval); + } while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])); + } + else if (fixedMax != null) { + var min; + // User set min, expand extent on the other side + do { + min = fixedMax - interval * splitNumber; + scale.setExtent(min, +fixedMax); + scale.setInterval(interval); + interval = increaseInterval(interval); + } while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])); + } + else { + var nicedSplitNumber = scale.getTicks().length - 1; + if (nicedSplitNumber > splitNumber) { + interval = increaseInterval(interval); + } + // PENDING + var center = Math.round((rawExtent[0] + rawExtent[1]) / 2 / interval) * interval; + var halfSplitNumber = Math.round(splitNumber / 2); + scale.setExtent( + numberUtil.round(center - halfSplitNumber * interval), + numberUtil.round(center + (splitNumber - halfSplitNumber) * interval) + ); + scale.setInterval(interval); + } + }); + }; + + /** + * Radar dimensions is based on the data + * @type {Array} + */ + Radar.dimensions = []; + + Radar.create = function (ecModel, api) { + var radarList = []; + ecModel.eachComponent('radar', function (radarModel) { + var radar = new Radar(radarModel, ecModel, api); + radarList.push(radar); + radarModel.coordinateSystem = radar; + }); + ecModel.eachSeriesByType('radar', function (radarSeries) { + if (radarSeries.get('coordinateSystem') === 'radar') { + // Inject coordinate system + radarSeries.coordinateSystem = radarList[radarSeries.get('radarIndex') || 0]; + } + }); + return radarList; + }; + + __webpack_require__(26).register('radar', Radar); + module.exports = Radar; + + +/***/ }, +/* 154 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function IndicatorAxis(dim, scale, radiusExtent) { + Axis.call(this, dim, scale, radiusExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'value'; + + this.angle = 0; + + /** + * Indicator name + * @type {string} + */ + this.name = ''; + /** + * @type {module:echarts/model/Model} + */ + this.model; + } + + zrUtil.inherits(IndicatorAxis, Axis); + + module.exports = IndicatorAxis; + + +/***/ }, +/* 155 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var axisDefault = __webpack_require__(128); + var valueAxisDefault = axisDefault.valueAxis; + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var axisModelCommonMixin = __webpack_require__(129); + + function defaultsShow(opt, show) { + return zrUtil.defaults({ + show: show + }, opt); + } + + var RadarModel = __webpack_require__(1).extendComponentModel({ + + type: 'radar', + + optionUpdated: function () { + var boundaryGap = this.get('boundaryGap'); + var splitNumber = this.get('splitNumber'); + var scale = this.get('scale'); + var axisLine = this.get('axisLine'); + var axisTick = this.get('axisTick'); + var axisLabel = this.get('axisLabel'); + var nameTextStyle = this.get('name.textStyle'); + var showName = this.get('name.show'); + var nameFormatter = this.get('name.formatter'); + var nameGap = this.get('nameGap'); + var indicatorModels = zrUtil.map(this.get('indicator') || [], function (indicatorOpt) { + // PENDING + if (indicatorOpt.max != null && indicatorOpt.max > 0 && !indicatorOpt.min) { + indicatorOpt.min = 0; + } + else if (indicatorOpt.min != null && indicatorOpt.min < 0 && !indicatorOpt.max) { + indicatorOpt.max = 0; + } + // Use same configuration + indicatorOpt = zrUtil.merge(zrUtil.clone(indicatorOpt), { + boundaryGap: boundaryGap, + splitNumber: splitNumber, + scale: scale, + axisLine: axisLine, + axisTick: axisTick, + axisLabel: axisLabel, + // Competitable with 2 and use text + name: indicatorOpt.text, + nameLocation: 'end', + nameGap: nameGap, + // min: 0, + nameTextStyle: nameTextStyle + }, false); + if (!showName) { + indicatorOpt.name = ''; + } + if (typeof nameFormatter === 'string') { + indicatorOpt.name = nameFormatter.replace('{value}', indicatorOpt.name); + } + else if (typeof nameFormatter === 'function') { + indicatorOpt.name = nameFormatter( + indicatorOpt.name, indicatorOpt + ); + } + return zrUtil.extend( + new Model(indicatorOpt, null, this.ecModel), + axisModelCommonMixin + ); + }, this); + this.getIndicatorModels = function () { + return indicatorModels; + }; + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + center: ['50%', '50%'], + + radius: '75%', + + startAngle: 90, + + name: { + show: true + // formatter: null + // textStyle: {} + }, + + boundaryGap: [0, 0], + + splitNumber: 5, + + nameGap: 15, + + scale: false, + + // Polygon or circle + shape: 'polygon', + + axisLine: zrUtil.merge( + { + lineStyle: { + color: '#bbb' + } + }, + valueAxisDefault.axisLine + ), + axisLabel: defaultsShow(valueAxisDefault.axisLabel, false), + axisTick: defaultsShow(valueAxisDefault.axisTick, false), + splitLine: defaultsShow(valueAxisDefault.splitLine, true), + splitArea: defaultsShow(valueAxisDefault.splitArea, true), + + // {text, min, max} + indicator: [] + } + }); + + module.exports = RadarModel; + + +/***/ }, +/* 156 */ +/***/ function(module, exports, __webpack_require__) { + + + + var AxisBuilder = __webpack_require__(132); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'radar', + + render: function (radarModel, ecModel, api) { + var group = this.group; + group.removeAll(); + + this._buildAxes(radarModel); + this._buildSplitLineAndArea(radarModel); + }, + + _buildAxes: function (radarModel) { + var radar = radarModel.coordinateSystem; + var indicatorAxes = radar.getIndicatorAxes(); + var axisBuilders = zrUtil.map(indicatorAxes, function (indicatorAxis) { + var axisBuilder = new AxisBuilder(indicatorAxis.model, { + position: [radar.cx, radar.cy], + rotation: indicatorAxis.angle, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1 + }); + return axisBuilder; + }); + + zrUtil.each(axisBuilders, function (axisBuilder) { + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + }, this); + }, + + _buildSplitLineAndArea: function (radarModel) { + var radar = radarModel.coordinateSystem; + var splitNumber = radarModel.get('splitNumber'); + var indicatorAxes = radar.getIndicatorAxes(); + if (!indicatorAxes.length) { + return; + } + var shape = radarModel.get('shape'); + var splitLineModel = radarModel.getModel('splitLine'); + var splitAreaModel = radarModel.getModel('splitArea'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + + var showSplitLine = splitLineModel.get('show'); + var showSplitArea = splitAreaModel.get('show'); + var splitLineColors = lineStyleModel.get('color'); + var splitAreaColors = areaStyleModel.get('color'); + + splitLineColors = zrUtil.isArray(splitLineColors) ? splitLineColors : [splitLineColors]; + splitAreaColors = zrUtil.isArray(splitAreaColors) ? splitAreaColors : [splitAreaColors]; + + var splitLines = []; + var splitAreas = []; + + function getColorIndex(areaOrLine, areaOrLineColorList, idx) { + var colorIndex = idx % areaOrLineColorList.length; + areaOrLine[colorIndex] = areaOrLine[colorIndex] || []; + return colorIndex; + } + + if (shape === 'circle') { + var ticksRadius = indicatorAxes[0].getTicksCoords(); + var cx = radar.cx; + var cy = radar.cy; + for (var i = 0; i < ticksRadius.length; i++) { + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColors, i); + splitLines[colorIndex].push(new graphic.Circle({ + shape: { + cx: cx, + cy: cy, + r: ticksRadius[i] + } + })); + } + if (showSplitArea && i < ticksRadius.length - 1) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i); + splitAreas[colorIndex].push(new graphic.Ring({ + shape: { + cx: cx, + cy: cy, + r0: ticksRadius[i], + r: ticksRadius[i + 1] + } + })); + } + } + } + // Polyyon + else { + var axesTicksPoints = zrUtil.map(indicatorAxes, function (indicatorAxis, idx) { + var ticksCoords = indicatorAxis.getTicksCoords(); + return zrUtil.map(ticksCoords, function (tickCoord) { + return radar.coordToPoint(tickCoord, idx); + }); + }); + + var prevPoints = []; + for (var i = 0; i <= splitNumber; i++) { + var points = []; + for (var j = 0; j < indicatorAxes.length; j++) { + points.push(axesTicksPoints[j][i]); + } + // Close + if (points[0]) { + points.push(points[0].slice()); + } + else { + if (true) { + console.error('Can\'t draw value axis ' + i); + continue; + } + } + if (showSplitLine) { + var colorIndex = getColorIndex(splitLines, splitLineColors, i); + splitLines[colorIndex].push(new graphic.Polyline({ + shape: { + points: points + } + })); + } + if (showSplitArea && prevPoints) { + var colorIndex = getColorIndex(splitAreas, splitAreaColors, i - 1); + splitAreas[colorIndex].push(new graphic.Polygon({ + shape: { + points: points.concat(prevPoints) + } + })); + } + prevPoints = points.slice().reverse(); + } + } + + var lineStyle = lineStyleModel.getLineStyle(); + var areaStyle = areaStyleModel.getAreaStyle(); + // Add splitArea before splitLine + zrUtil.each(splitAreas, function (splitAreas, idx) { + this.group.add(graphic.mergePath( + splitAreas, { + style: zrUtil.defaults({ + stroke: 'none', + fill: splitAreaColors[idx % splitAreaColors.length] + }, areaStyle), + silent: true + } + )); + }, this); + + zrUtil.each(splitLines, function (splitLines, idx) { + this.group.add(graphic.mergePath( + splitLines, { + style: zrUtil.defaults({ + fill: 'none', + stroke: splitLineColors[idx % splitLineColors.length] + }, lineStyle), + silent: true + } + )); + }, this); + + } + }); + + +/***/ }, +/* 157 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var zrUtil = __webpack_require__(4); + + var RadarSeries = SeriesModel.extend({ + + type: 'series.radar', + + dependencies: ['radar'], + + + // Overwrite + init: function (option) { + RadarSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + }, + + getInitialData: function (option, ecModel) { + var data = option.data || []; + var dimensions = completeDimensions( + [], data, [], 'indicator_' + ); + var list = new List(dimensions, this); + list.initData(data); + return list; + }, + + formatTooltip: function (dataIndex) { + var value = this.getRawValue(dataIndex); + var coordSys = this.coordinateSystem; + var indicatorAxes = coordSys.getIndicatorAxes(); + return (this._data.getName(dataIndex) == '' ? this.name : this._data.getName(dataIndex)) + '
' + + zrUtil.map(indicatorAxes, function (axis, idx) { + return axis.name + ' : ' + value[idx]; + }).join('
'); + }, + + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: 'radar', + legendHoverLink: true, + radarIndex: 0, + lineStyle: { + normal: { + width: 2, + type: 'solid' + } + }, + label: { + normal: { + position: 'top' + } + }, + // areaStyle: { + // }, + // itemStyle: {} + symbol: 'emptyCircle', + symbolSize: 4 + // symbolRotate: null + } + }); + + module.exports = RadarSeries; + + +/***/ }, +/* 158 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + + function normalizeSymbolSize(symbolSize) { + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + module.exports = __webpack_require__(1).extendChartView({ + type: 'radar', + + render: function (seriesModel, ecModel, api) { + var polar = seriesModel.coordinateSystem; + var group = this.group; + + var data = seriesModel.getData(); + var oldData = this._data; + + function createSymbol(data, idx) { + var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; + var color = data.getItemVisual(idx, 'color'); + if (symbolType === 'none') { + return; + } + var symbolPath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + symbolPath.attr({ + style: { + strokeNoScale: true + }, + z2: 100, + scale: normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')) + }); + return symbolPath; + } + + function updateSymbols(oldPoints, newPoints, symbolGroup, data, idx, isInit) { + // Simply rerender all + symbolGroup.removeAll(); + for (var i = 0; i < newPoints.length - 1; i++) { + var symbolPath = createSymbol(data, idx); + if (symbolPath) { + symbolPath.__dimIdx = i; + if (oldPoints[i]) { + symbolPath.attr('position', oldPoints[i]); + graphic[isInit ? 'initProps' : 'updateProps']( + symbolPath, { + position: newPoints[i] + }, seriesModel, idx + ); + } + else { + symbolPath.attr('position', newPoints[i]); + } + symbolGroup.add(symbolPath); + } + } + } + + function getInitialPoints(points) { + return zrUtil.map(points, function (pt) { + return [polar.cx, polar.cy]; + }); + } + data.diff(oldData) + .add(function (idx) { + var points = data.getItemLayout(idx); + if (!points) { + return; + } + var polygon = new graphic.Polygon(); + var polyline = new graphic.Polyline(); + var target = { + shape: { + points: points + } + }; + polygon.shape.points = getInitialPoints(points); + polyline.shape.points = getInitialPoints(points); + graphic.initProps(polygon, target, seriesModel, idx); + graphic.initProps(polyline, target, seriesModel, idx); + + var itemGroup = new graphic.Group(); + var symbolGroup = new graphic.Group(); + itemGroup.add(polyline); + itemGroup.add(polygon); + itemGroup.add(symbolGroup); + + updateSymbols( + polyline.shape.points, points, symbolGroup, data, idx, true + ); + + data.setItemGraphicEl(idx, itemGroup); + }) + .update(function (newIdx, oldIdx) { + var itemGroup = oldData.getItemGraphicEl(oldIdx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + var target = { + shape: { + points: data.getItemLayout(newIdx) + } + }; + if (!target.shape.points) { + return; + } + updateSymbols( + polyline.shape.points, target.shape.points, symbolGroup, data, newIdx, false + ); + + graphic.updateProps(polyline, target, seriesModel); + graphic.updateProps(polygon, target, seriesModel); + + data.setItemGraphicEl(newIdx, itemGroup); + }) + .remove(function (idx) { + group.remove(oldData.getItemGraphicEl(idx)); + }) + .execute(); + + data.eachItemGraphicEl(function (itemGroup, idx) { + var itemModel = data.getItemModel(idx); + var polyline = itemGroup.childAt(0); + var polygon = itemGroup.childAt(1); + var symbolGroup = itemGroup.childAt(2); + var color = data.getItemVisual(idx, 'color'); + + group.add(itemGroup); + + polyline.useStyle( + zrUtil.defaults( + itemModel.getModel('lineStyle.normal').getLineStyle(), + { + fill: 'none', + stroke: color + } + ) + ); + polyline.hoverStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + + var areaStyleModel = itemModel.getModel('areaStyle.normal'); + var hoverAreaStyleModel = itemModel.getModel('areaStyle.emphasis'); + var polygonIgnore = areaStyleModel.isEmpty() && areaStyleModel.parentModel.isEmpty(); + var hoverPolygonIgnore = hoverAreaStyleModel.isEmpty() && hoverAreaStyleModel.parentModel.isEmpty(); + + hoverPolygonIgnore = hoverPolygonIgnore && polygonIgnore; + polygon.ignore = polygonIgnore; + + polygon.useStyle( + zrUtil.defaults( + areaStyleModel.getAreaStyle(), + { + fill: color, + opacity: 0.7 + } + ) + ); + polygon.hoverStyle = hoverAreaStyleModel.getAreaStyle(); + + var itemStyle = itemModel.getModel('itemStyle.normal').getItemStyle(['color']); + var itemHoverStyle = itemModel.getModel('itemStyle.emphasis').getItemStyle(); + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + symbolGroup.eachChild(function (symbolPath) { + symbolPath.setStyle(itemStyle); + symbolPath.hoverStyle = zrUtil.clone(itemHoverStyle); + + var defaultText = data.get(data.dimensions[symbolPath.__dimIdx], idx); + graphic.setText(symbolPath.style, labelModel, color); + symbolPath.setStyle({ + text: labelModel.get('show') ? zrUtil.retrieve( + seriesModel.getFormattedLabel( + idx, 'normal', null, symbolPath.__dimIdx + ), + defaultText + ) : '' + }); + + graphic.setText(symbolPath.hoverStyle, labelHoverModel, color); + symbolPath.hoverStyle.text = labelHoverModel.get('show') ? zrUtil.retrieve( + seriesModel.getFormattedLabel( + idx, 'emphasis', null, symbolPath.__dimIdx + ), + defaultText + ) : ''; + }); + + function onEmphasis() { + polygon.attr('ignore', hoverPolygonIgnore); + } + + function onNormal() { + polygon.attr('ignore', polygonIgnore); + } + + itemGroup.off('mouseover').off('mouseout').off('normal').off('emphasis'); + itemGroup.on('emphasis', onEmphasis) + .on('mouseover', onEmphasis) + .on('normal', onNormal) + .on('mouseout', onNormal); + + graphic.setHoverStyle(itemGroup); + }); + + this._data = data; + }, + + remove: function () { + this.group.removeAll(); + this._data = null; + } + }); + + +/***/ }, +/* 159 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('radar', function (seriesModel) { + var data = seriesModel.getData(); + var points = []; + var coordSys = seriesModel.coordinateSystem; + if (!coordSys) { + return; + } + + function pointsConverter(val, idx) { + points[idx] = points[idx] || []; + points[idx][i] = coordSys.dataToPoint(val, i); + } + for (var i = 0; i < coordSys.getIndicatorAxes().length; i++) { + var dim = data.dimensions[i]; + data.each(dim, pointsConverter); + } + + data.each(function (idx) { + // Close polygon + points[idx][0] && points[idx].push(points[idx][0].slice()); + data.setItemLayout(idx, points[idx]); + }); + }); + }; + + +/***/ }, +/* 160 */ +/***/ function(module, exports, __webpack_require__) { + + // Backward compat for radar chart in 2 + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + var polarOptArr = option.polar; + if (polarOptArr) { + if (!zrUtil.isArray(polarOptArr)) { + polarOptArr = [polarOptArr]; + } + var polarNotRadar = []; + zrUtil.each(polarOptArr, function (polarOpt, idx) { + if (polarOpt.indicator) { + if (polarOpt.type && !polarOpt.shape) { + polarOpt.shape = polarOpt.type; + } + option.radar = option.radar || []; + if (!zrUtil.isArray(option.radar)) { + option.radar = [option.radar]; + } + option.radar.push(polarOpt); + } + else { + polarNotRadar.push(polarOpt); + } + }); + option.polar = polarNotRadar; + } + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt.type === 'radar' && seriesOpt.polarIndex) { + seriesOpt.radarIndex = seriesOpt.polarIndex; + } + }); + }; + + +/***/ }, +/* 161 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var PRIORITY = echarts.PRIORITY; + + __webpack_require__(162); + + __webpack_require__(172); + + __webpack_require__(176); + + __webpack_require__(163); + + echarts.registerLayout(__webpack_require__(178)); + + echarts.registerVisual(__webpack_require__(179)); + + echarts.registerProcessor(PRIORITY.PROCESSOR.STATISTIC, __webpack_require__(180)); + + echarts.registerPreprocessor(__webpack_require__(181)); + + __webpack_require__(142)('map', [{ + type: 'mapToggleSelect', + event: 'mapselectchanged', + method: 'toggleSelected' + }, { + type: 'mapSelect', + event: 'mapselected', + method: 'select' + }, { + type: 'mapUnSelect', + event: 'mapunselected', + method: 'unSelect' + }]); + + +/***/ }, +/* 162 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var SeriesModel = __webpack_require__(28); + var zrUtil = __webpack_require__(4); + var completeDimensions = __webpack_require__(102); + + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var dataSelectableMixin = __webpack_require__(140); + + var geoCreator = __webpack_require__(163); + + var MapSeries = SeriesModel.extend({ + + type: 'series.map', + + layoutMode: 'box', + + /** + * Only first map series of same mapType will drawMap + * @type {boolean} + */ + needsDrawMap: false, + + /** + * Group of all map series with same mapType + * @type {boolean} + */ + seriesGroup: [], + + init: function (option) { + + option = this._fillOption(option, option.map); + this.option = option; + + MapSeries.superApply(this, 'init', arguments); + + this.updateSelectedMap(option.data); + }, + + getInitialData: function (option) { + var dimensions = completeDimensions(['value'], option.data || []); + + var list = new List(dimensions, this); + + list.initData(option.data); + + return list; + }, + + mergeOption: function (newOption) { + if (newOption.data) { + newOption = this._fillOption(newOption, this.option.map); + } + + MapSeries.superCall(this, 'mergeOption', newOption); + + this.updateSelectedMap(this.option.data); + }, + + _fillOption: function (option, mapName) { + // Shallow clone + option = zrUtil.extend({}, option); + + option.data = geoCreator.getFilledRegions(option.data, mapName); + + return option; + }, + + getRawValue: function (dataIndex) { + // Use value stored in data instead because it is calculated from multiple series + // FIXME Provide all value of multiple series ? + return this._data.get('value', dataIndex); + }, + + /** + * Get model of region + * @param {string} name + * @return {module:echarts/model/Model} + */ + getRegionModel: function (regionName) { + var data = this.getData(); + return data.getItemModel(data.indexOfName(regionName)); + }, + + /** + * Map tooltip formatter + * + * @param {number} dataIndex + */ + formatTooltip: function (dataIndex) { + // FIXME orignalData and data is a bit confusing + var data = this.getData(); + var formattedValue = addCommas(this.getRawValue(dataIndex)); + var name = data.getName(dataIndex); + + var seriesGroup = this.seriesGroup; + var seriesNames = []; + for (var i = 0; i < seriesGroup.length; i++) { + var otherIndex = seriesGroup[i].originalData.indexOfName(name); + if (!isNaN(seriesGroup[i].originalData.get('value', otherIndex))) { + seriesNames.push( + encodeHTML(seriesGroup[i].name) + ); + } + } + + return seriesNames.join(', ') + '
' + + name + ' : ' + formattedValue; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 2, + coordinateSystem: 'geo', + // 各省的 map 暂时都用中文 + map: 'china', + + // 'center' | 'left' | 'right' | 'x%' | {number} + left: 'center', + // 'center' | 'top' | 'bottom' | 'x%' | {number} + top: 'center', + // right + // bottom + // width: + // height + + // Aspect is width / height. Inited to be geoJson bbox aspect + // This parameter is used for scale this aspect + aspectScale: 0.75, + + ///// Layout with center and size + // If you wan't to put map in a fixed size box with right aspect ratio + // This two properties may more conveninet + // layoutCenter: [50%, 50%] + // layoutSize: 100 + + + // 数值合并方式,默认加和,可选为: + // 'sum' | 'average' | 'max' | 'min' + // mapValueCalculation: 'sum', + // 地图数值计算结果小数精度 + // mapValuePrecision: 0, + + + // 显示图例颜色标识(系列标识的小圆点),图例开启时有效 + showLegendSymbol: true, + // 选择模式,默认关闭,可选single,multiple + // selectedMode: false, + dataRangeHoverLink: true, + // 是否开启缩放及漫游模式 + // roam: false, + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + label: { + normal: { + show: false, + textStyle: { + color: '#000' + } + }, + emphasis: { + show: true, + textStyle: { + color: 'rgb(100,0,0)' + } + } + }, + // scaleLimit: null, + itemStyle: { + normal: { + // color: 各异, + borderWidth: 0.5, + borderColor: '#444', + areaColor: '#eee' + }, + // 也是选中样式 + emphasis: { + areaColor: 'rgba(255,215,0,0.8)' + } + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + } + }); + + zrUtil.mixin(MapSeries, dataSelectableMixin); + + module.exports = MapSeries; + + +/***/ }, +/* 163 */ +/***/ function(module, exports, __webpack_require__) { + + + + var Geo = __webpack_require__(164); + + var layout = __webpack_require__(21); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var mapDataStores = {}; + + /** + * Resize method bound to the geo + * @param {module:echarts/coord/geo/GeoModel|module:echarts/chart/map/MapModel} geoModel + * @param {module:echarts/ExtensionAPI} api + */ + function resizeGeo (geoModel, api) { + var rect = this.getBoundingRect(); + + var boxLayoutOption; + + var center = geoModel.get('layoutCenter'); + var size = geoModel.get('layoutSize'); + + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + + var aspectScale = geoModel.get('aspectScale') || 0.75; + var aspect = rect.width / rect.height * aspectScale; + + var useCenterAndSize = false; + if (center && size) { + center = [ + numberUtil.parsePercent(center[0], viewWidth), + numberUtil.parsePercent(center[1], viewHeight) + ]; + size = numberUtil.parsePercent(size, Math.min(viewWidth, viewHeight)); + + if (!isNaN(center[0]) && !isNaN(center[1]) && !isNaN(size)) { + useCenterAndSize = true; + } + else { + if (true) { + console.warn('Given layoutCenter or layoutSize data are invalid. Use left/top/width/height instead.'); + } + } + } + + var viewRect; + if (useCenterAndSize) { + var viewRect = {}; + if (aspect > 1) { + // Width is same with size + viewRect.width = size; + viewRect.height = size / aspect; + } + else { + viewRect.height = size; + viewRect.width = size * aspect; + } + viewRect.y = center[1] - viewRect.height / 2; + viewRect.x = center[0] - viewRect.width / 2; + } + else { + // Use left/top/width/height + boxLayoutOption = geoModel.getBoxLayoutParams(); + + // 0.75 rate + boxLayoutOption.aspect = aspect; + + viewRect = layout.getLayoutRect(boxLayoutOption, { + width: viewWidth, + height: viewHeight + }); + } + + this.setViewRect(viewRect.x, viewRect.y, viewRect.width, viewRect.height); + + this.setCenter(geoModel.get('center')); + this.setZoom(geoModel.get('zoom')); + } + + /** + * @param {module:echarts/coord/Geo} geo + * @param {module:echarts/model/Model} model + * @inner + */ + function setGeoCoords(geo, model) { + zrUtil.each(model.get('geoCoord'), function (geoCoord, name) { + geo.addGeoCoord(name, geoCoord); + }); + } + + if (true) { + var mapNotExistsError = function (name) { + console.error('Map ' + name + ' not exists. You can download map file on http://echarts.baidu.com/download-map.html'); + }; + } + + var geoCreator = { + + // For deciding which dimensions to use when creating list data + dimensions: Geo.prototype.dimensions, + + create: function (ecModel, api) { + var geoList = []; + + // FIXME Create each time may be slow + ecModel.eachComponent('geo', function (geoModel, idx) { + var name = geoModel.get('map'); + var mapData = mapDataStores[name]; + if (true) { + if (!mapData) { + mapNotExistsError(name); + } + } + var geo = new Geo( + name + idx, name, + mapData && mapData.geoJson, mapData && mapData.specialAreas, + geoModel.get('nameMap') + ); + geo.zoomLimit = geoModel.get('scaleLimit'); + geoList.push(geo); + + setGeoCoords(geo, geoModel); + + geoModel.coordinateSystem = geo; + geo.model = geoModel; + + // Inject resize method + geo.resize = resizeGeo; + + geo.resize(geoModel, api); + }); + + ecModel.eachSeries(function (seriesModel) { + var coordSys = seriesModel.get('coordinateSystem'); + if (coordSys === 'geo') { + var geoIndex = seriesModel.get('geoIndex') || 0; + seriesModel.coordinateSystem = geoList[geoIndex]; + } + }); + + // If has map series + var mapModelGroupBySeries = {}; + + ecModel.eachSeriesByType('map', function (seriesModel) { + var mapType = seriesModel.get('map'); + + mapModelGroupBySeries[mapType] = mapModelGroupBySeries[mapType] || []; + + mapModelGroupBySeries[mapType].push(seriesModel); + }); + + zrUtil.each(mapModelGroupBySeries, function (mapSeries, mapType) { + var mapData = mapDataStores[mapType]; + if (true) { + if (!mapData) { + mapNotExistsError(mapSeries[0].get('map')); + } + } + + var nameMapList = zrUtil.map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('nameMap'); + }); + var geo = new Geo( + mapType, mapType, + mapData && mapData.geoJson, mapData && mapData.specialAreas, + zrUtil.mergeAll(nameMapList) + ); + geo.zoomLimit = zrUtil.retrieve.apply(null, zrUtil.map(mapSeries, function (singleMapSeries) { + return singleMapSeries.get('scaleLimit'); + })); + geoList.push(geo); + + // Inject resize method + geo.resize = resizeGeo; + + geo.resize(mapSeries[0], api); + + zrUtil.each(mapSeries, function (singleMapSeries) { + singleMapSeries.coordinateSystem = geo; + + setGeoCoords(geo, singleMapSeries); + }); + }); + + return geoList; + }, + + /** + * @param {string} mapName + * @param {Object|string} geoJson + * @param {Object} [specialAreas] + * + * @example + * $.get('USA.json', function (geoJson) { + * echarts.registerMap('USA', geoJson); + * // Or + * echarts.registerMap('USA', { + * geoJson: geoJson, + * specialAreas: {} + * }) + * }); + */ + registerMap: function (mapName, geoJson, specialAreas) { + if (geoJson.geoJson && !geoJson.features) { + specialAreas = geoJson.specialAreas; + geoJson = geoJson.geoJson; + } + if (typeof geoJson === 'string') { + geoJson = (typeof JSON !== 'undefined' && JSON.parse) + ? JSON.parse(geoJson) : (new Function('return (' + geoJson + ');'))(); + } + mapDataStores[mapName] = { + geoJson: geoJson, + specialAreas: specialAreas + }; + }, + + /** + * @param {string} mapName + * @return {Object} + */ + getMap: function (mapName) { + return mapDataStores[mapName]; + }, + + /** + * Fill given regions array + * @param {Array.} originRegionArr + * @param {string} mapName + * @return {Array} + */ + getFilledRegions: function (originRegionArr, mapName) { + // Not use the original + var regionsArr = (originRegionArr || []).slice(); + + var map = geoCreator.getMap(mapName); + var geoJson = map && map.geoJson; + if (!geoJson) { + if (true) { + mapNotExistsError(mapName); + } + return originRegionArr; + } + + var dataNameMap = {}; + var features = geoJson.features; + for (var i = 0; i < regionsArr.length; i++) { + dataNameMap[regionsArr[i].name] = regionsArr[i]; + } + + for (var i = 0; i < features.length; i++) { + var name = features[i].properties.name; + if (!dataNameMap[name]) { + regionsArr.push({ + name: name + }); + } + } + return regionsArr; + } + }; + + // Inject methods into echarts + var echarts = __webpack_require__(1); + + echarts.registerMap = geoCreator.registerMap; + + echarts.getMap = geoCreator.getMap; + + // TODO + echarts.loadMap = function () {}; + + echarts.registerCoordinateSystem('geo', geoCreator); + + module.exports = geoCreator; + + +/***/ }, +/* 164 */ +/***/ function(module, exports, __webpack_require__) { + + + + var parseGeoJson = __webpack_require__(165); + + var zrUtil = __webpack_require__(4); + + var BoundingRect = __webpack_require__(9); + + var View = __webpack_require__(168); + + + // Geo fix functions + var geoFixFuncs = [ + __webpack_require__(169), + __webpack_require__(170), + __webpack_require__(171) + ]; + + /** + * [Geo description] + * @param {string} name Geo name + * @param {string} map Map type + * @param {Object} geoJson + * @param {Object} [specialAreas] + * Specify the positioned areas by left, top, width, height + * @param {Object.} [nameMap] + * Specify name alias + */ + function Geo(name, map, geoJson, specialAreas, nameMap) { + + View.call(this, name); + + /** + * Map type + * @type {string} + */ + this.map = map; + + this._nameCoordMap = {}; + + this.loadGeoJson(geoJson, specialAreas, nameMap); + } + + Geo.prototype = { + + constructor: Geo, + + type: 'geo', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['lng', 'lat'], + + /** + * If contain given lng,lat coord + * @param {Array.} + * @readOnly + */ + containCoord: function (coord) { + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + if (regions[i].contain(coord)) { + return true; + } + } + return false; + }, + /** + * @param {Object} geoJson + * @param {Object} [specialAreas] + * Specify the positioned areas by left, top, width, height + * @param {Object.} [nameMap] + * Specify name alias + */ + loadGeoJson: function (geoJson, specialAreas, nameMap) { + // https://jsperf.com/try-catch-performance-overhead + try { + this.regions = geoJson ? parseGeoJson(geoJson) : []; + } + catch (e) { + throw 'Invalid geoJson format\n' + e; + } + specialAreas = specialAreas || {}; + nameMap = nameMap || {}; + var regions = this.regions; + var regionsMap = {}; + for (var i = 0; i < regions.length; i++) { + var regionName = regions[i].name; + // Try use the alias in nameMap + regionName = nameMap[regionName] || regionName; + regions[i].name = regionName; + + regionsMap[regionName] = regions[i]; + // Add geoJson + this.addGeoCoord(regionName, regions[i].center); + + // Some area like Alaska in USA map needs to be tansformed + // to look better + var specialArea = specialAreas[regionName]; + if (specialArea) { + regions[i].transformTo( + specialArea.left, specialArea.top, specialArea.width, specialArea.height + ); + } + } + + this._regionsMap = regionsMap; + + this._rect = null; + + zrUtil.each(geoFixFuncs, function (fixFunc) { + fixFunc(this); + }, this); + }, + + // Overwrite + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + + rect = rect.clone(); + // Longitute is inverted + rect.y = -rect.y - rect.height; + + var viewTransform = this._viewTransform; + + viewTransform.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + viewTransform.decomposeTransform(); + + var scale = viewTransform.scale; + scale[1] = -scale[1]; + + viewTransform.updateTransform(); + + this._updateTransform(); + }, + + /** + * @param {string} name + * @return {module:echarts/coord/geo/Region} + */ + getRegion: function (name) { + return this._regionsMap[name]; + }, + + getRegionByCoord: function (coord) { + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + if (regions[i].contain(coord)) { + return regions[i]; + } + } + }, + + /** + * Add geoCoord for indexing by name + * @param {string} name + * @param {Array.} geoCoord + */ + addGeoCoord: function (name, geoCoord) { + this._nameCoordMap[name] = geoCoord; + }, + + /** + * Get geoCoord by name + * @param {string} name + * @return {Array.} + */ + getGeoCoord: function (name) { + return this._nameCoordMap[name]; + }, + + // Overwrite + getBoundingRect: function () { + if (this._rect) { + return this._rect; + } + var rect; + + var regions = this.regions; + for (var i = 0; i < regions.length; i++) { + var regionRect = regions[i].getBoundingRect(); + rect = rect || regionRect.clone(); + rect.union(regionRect); + } + // FIXME Always return new ? + return (this._rect = rect || new BoundingRect(0, 0, 0, 0)); + }, + + /** + * Convert series data to a list of points + * @param {module:echarts/data/List} data + * @param {boolean} stack + * @return {Array} + * Return list of points. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data) { + var item = []; + return data.mapArray(['lng', 'lat'], function (lon, lat) { + item[0] = lon; + item[1] = lat; + return this.dataToPoint(item); + }, this); + }, + + // Overwrite + /** + * @param {string|Array.} data + * @return {Array.} + */ + dataToPoint: function (data) { + if (typeof data === 'string') { + // Map area name to geoCoord + data = this.getGeoCoord(data); + } + if (data) { + return View.prototype.dataToPoint.call(this, data); + } + } + }; + + zrUtil.mixin(Geo, View); + + module.exports = Geo; + + +/***/ }, +/* 165 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parse and decode geo json + * @module echarts/coord/geo/parseGeoJson + */ + + + var zrUtil = __webpack_require__(4); + + var Region = __webpack_require__(166); + + function decode(json) { + if (!json.UTF8Encoding) { + return json; + } + var features = json.features; + + for (var f = 0; f < features.length; f++) { + var feature = features[f]; + var geometry = feature.geometry; + var coordinates = geometry.coordinates; + var encodeOffsets = geometry.encodeOffsets; + + for (var c = 0; c < coordinates.length; c++) { + var coordinate = coordinates[c]; + + if (geometry.type === 'Polygon') { + coordinates[c] = decodePolygon( + coordinate, + encodeOffsets[c] + ); + } + else if (geometry.type === 'MultiPolygon') { + for (var c2 = 0; c2 < coordinate.length; c2++) { + var polygon = coordinate[c2]; + coordinate[c2] = decodePolygon( + polygon, + encodeOffsets[c][c2] + ); + } + } + } + } + // Has been decoded + json.UTF8Encoding = false; + return json; + } + + function decodePolygon(coordinate, encodeOffsets) { + var result = []; + var prevX = encodeOffsets[0]; + var prevY = encodeOffsets[1]; + + for (var i = 0; i < coordinate.length; i += 2) { + var x = coordinate.charCodeAt(i) - 64; + var y = coordinate.charCodeAt(i + 1) - 64; + // ZigZag decoding + x = (x >> 1) ^ (-(x & 1)); + y = (y >> 1) ^ (-(y & 1)); + // Delta deocding + x += prevX; + y += prevY; + + prevX = x; + prevY = y; + // Dequantize + result.push([x / 1024, y / 1024]); + } + + return result; + } + + /** + * @inner + */ + function flattern2D(array) { + var ret = []; + for (var i = 0; i < array.length; i++) { + for (var k = 0; k < array[i].length; k++) { + ret.push(array[i][k]); + } + } + return ret; + } + + /** + * @alias module:echarts/coord/geo/parseGeoJson + * @param {Object} geoJson + * @return {module:zrender/container/Group} + */ + module.exports = function (geoJson) { + + decode(geoJson); + + return zrUtil.map(zrUtil.filter(geoJson.features, function (featureObj) { + // Output of mapshaper may have geometry null + return featureObj.geometry && featureObj.properties; + }), function (featureObj) { + var properties = featureObj.properties; + var geometry = featureObj.geometry; + + var coordinates = geometry.coordinates; + + if (geometry.type === 'MultiPolygon') { + coordinates = flattern2D(coordinates); + } + + return new Region( + properties.name, + coordinates, + properties.cp + ); + }); + }; + + +/***/ }, +/* 166 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/coord/geo/Region + */ + + + var polygonContain = __webpack_require__(167); + + var BoundingRect = __webpack_require__(9); + + var bbox = __webpack_require__(51); + var vec2 = __webpack_require__(10); + + /** + * @param {string} name + * @param {Array} contours + * @param {Array.} cp + */ + function Region(name, contours, cp) { + + /** + * @type {string} + * @readOnly + */ + this.name = name; + + /** + * @type {Array.} + * @readOnly + */ + this.contours = contours; + + if (!cp) { + var rect = this.getBoundingRect(); + cp = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } + else { + cp = [cp[0], cp[1]]; + } + /** + * @type {Array.} + */ + this.center = cp; + } + + Region.prototype = { + + constructor: Region, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getBoundingRect: function () { + var rect = this._rect; + if (rect) { + return rect; + } + + var MAX_NUMBER = Number.MAX_VALUE; + var min = [MAX_NUMBER, MAX_NUMBER]; + var max = [-MAX_NUMBER, -MAX_NUMBER]; + var min2 = []; + var max2 = []; + var contours = this.contours; + for (var i = 0; i < contours.length; i++) { + bbox.fromPoints(contours[i], min2, max2); + vec2.min(min, min, min2); + vec2.max(max, max, max2); + } + // No data + if (i === 0) { + min[0] = min[1] = max[0] = max[1] = 0; + } + + return (this._rect = new BoundingRect( + min[0], min[1], max[0] - min[0], max[1] - min[1] + )); + }, + + /** + * @param {} coord + * @return {boolean} + */ + contain: function (coord) { + var rect = this.getBoundingRect(); + var contours = this.contours; + if (rect.contain(coord[0], coord[1])) { + for (var i = 0, len = contours.length; i < len; i++) { + if (polygonContain.contain(contours[i], coord[0], coord[1])) { + return true; + } + } + } + return false; + }, + + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + var aspect = rect.width / rect.height; + if (!width) { + width = aspect * height; + } + else if (!height) { + height = width / aspect ; + } + var target = new BoundingRect(x, y, width, height); + var transform = rect.calculateTransform(target); + var contours = this.contours; + for (var i = 0; i < contours.length; i++) { + for (var p = 0; p < contours[i].length; p++) { + vec2.applyTransform(contours[i][p], contours[i][p], transform); + } + } + rect = this._rect; + rect.copy(target); + // Update center + this.center = [ + rect.x + rect.width / 2, + rect.y + rect.height / 2 + ]; + } + }; + + module.exports = Region; + + +/***/ }, +/* 167 */ +/***/ function(module, exports, __webpack_require__) { + + + + var windingLine = __webpack_require__(58); + + var EPSILON = 1e-8; + + function isAroundEqual(a, b) { + return Math.abs(a - b) < EPSILON; + } + + function contain(points, x, y) { + var w = 0; + var p = points[0]; + + if (!p) { + return false; + } + + for (var i = 1; i < points.length; i++) { + var p2 = points[i]; + w += windingLine(p[0], p[1], p2[0], p2[1], x, y); + p = p2; + } + + // Close polygon + var p0 = points[0]; + if (!isAroundEqual(p[0], p0[0]) || !isAroundEqual(p[1], p0[1])) { + w += windingLine(p[0], p[1], p0[0], p0[1], x, y); + } + + return w !== 0; + } + + + module.exports = { + contain: contain + }; + + +/***/ }, +/* 168 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Simple view coordinate system + * Mapping given x, y to transformd view x, y + */ + + + var vector = __webpack_require__(10); + var matrix = __webpack_require__(11); + + var Transformable = __webpack_require__(34); + var zrUtil = __webpack_require__(4); + + var BoundingRect = __webpack_require__(9); + + var v2ApplyTransform = vector.applyTransform; + + // Dummy transform node + function TransformDummy() { + Transformable.call(this); + } + zrUtil.mixin(TransformDummy, Transformable); + + function View(name) { + /** + * @type {string} + */ + this.name = name; + + /** + * @type {Object} + */ + this.zoomLimit; + + Transformable.call(this); + + this._roamTransform = new TransformDummy(); + + this._viewTransform = new TransformDummy(); + + this._center; + this._zoom; + } + + View.prototype = { + + constructor: View, + + type: 'view', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['x', 'y'], + + /** + * Set bounding rect + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + + // PENDING to getRect + setBoundingRect: function (x, y, width, height) { + this._rect = new BoundingRect(x, y, width, height); + return this._rect; + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + // PENDING to getRect + getBoundingRect: function () { + return this._rect; + }, + + /** + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + setViewRect: function (x, y, width, height) { + width = width; + height = height; + this.transformTo(x, y, width, height); + this._viewRect = new BoundingRect(x, y, width, height); + }, + + /** + * Transformed to particular position and size + * @param {number} x + * @param {number} y + * @param {number} width + * @param {number} height + */ + transformTo: function (x, y, width, height) { + var rect = this.getBoundingRect(); + var viewTransform = this._viewTransform; + + viewTransform.transform = rect.calculateTransform( + new BoundingRect(x, y, width, height) + ); + + viewTransform.decomposeTransform(); + + this._updateTransform(); + }, + + /** + * Set center of view + * @param {Array.} [centerCoord] + */ + setCenter: function (centerCoord) { + if (!centerCoord) { + return; + } + this._center = centerCoord; + + this._updateCenterAndZoom(); + }, + + /** + * @param {number} zoom + */ + setZoom: function (zoom) { + zoom = zoom || 1; + + var zoomLimit = this.zoomLimit; + if (zoomLimit) { + if (zoomLimit.max != null) { + zoom = Math.min(zoomLimit.max, zoom); + } + if (zoomLimit.min != null) { + zoom = Math.max(zoomLimit.min, zoom); + } + } + this._zoom = zoom; + + this._updateCenterAndZoom(); + }, + + /** + * Get default center without roam + */ + getDefaultCenter: function () { + // Rect before any transform + var rawRect = this.getBoundingRect(); + var cx = rawRect.x + rawRect.width / 2; + var cy = rawRect.y + rawRect.height / 2; + + return [cx, cy]; + }, + + getCenter: function () { + return this._center || this.getDefaultCenter(); + }, + + getZoom: function () { + return this._zoom || 1; + }, + + /** + * @return {Array.} data + * @return {Array.} + */ + dataToPoint: function (data) { + var transform = this.transform; + return transform + ? v2ApplyTransform([], data, transform) + : [data[0], data[1]]; + }, + + /** + * Convert a (x, y) point to (lon, lat) data + * @param {Array.} point + * @return {Array.} + */ + pointToData: function (point) { + var invTransform = this.invTransform; + return invTransform + ? v2ApplyTransform([], point, invTransform) + : [point[0], point[1]]; + } + + /** + * @return {number} + */ + // getScalarScale: function () { + // // Use determinant square root of transform to mutiply scalar + // var m = this.transform; + // var det = Math.sqrt(Math.abs(m[0] * m[3] - m[2] * m[1])); + // return det; + // } + }; + + zrUtil.mixin(View, Transformable); + + module.exports = View; + + +/***/ }, +/* 169 */ +/***/ function(module, exports, __webpack_require__) { + + // Fix for 南海诸岛 + + + var Region = __webpack_require__(166); + + var geoCoord = [126, 25]; + + var points = [ + [[0,3.5],[7,11.2],[15,11.9],[30,7],[42,0.7],[52,0.7], + [56,7.7],[59,0.7],[64,0.7],[64,0],[5,0],[0,3.5]], + [[13,16.1],[19,14.7],[16,21.7],[11,23.1],[13,16.1]], + [[12,32.2],[14,38.5],[15,38.5],[13,32.2],[12,32.2]], + [[16,47.6],[12,53.2],[13,53.2],[18,47.6],[16,47.6]], + [[6,64.4],[8,70],[9,70],[8,64.4],[6,64.4]], + [[23,82.6],[29,79.8],[30,79.8],[25,82.6],[23,82.6]], + [[37,70.7],[43,62.3],[44,62.3],[39,70.7],[37,70.7]], + [[48,51.1],[51,45.5],[53,45.5],[50,51.1],[48,51.1]], + [[51,35],[51,28.7],[53,28.7],[53,35],[51,35]], + [[52,22.4],[55,17.5],[56,17.5],[53,22.4],[52,22.4]], + [[58,12.6],[62,7],[63,7],[60,12.6],[58,12.6]], + [[0,3.5],[0,93.1],[64,93.1],[64,0],[63,0],[63,92.4], + [1,92.4],[1,3.5],[0,3.5]] + ]; + for (var i = 0; i < points.length; i++) { + for (var k = 0; k < points[i].length; k++) { + points[i][k][0] /= 10.5; + points[i][k][1] /= -10.5 / 0.75; + + points[i][k][0] += geoCoord[0]; + points[i][k][1] += geoCoord[1]; + } + } + module.exports = function (geo) { + if (geo.map === 'china') { + geo.regions.push(new Region( + '南海诸岛', points, geoCoord + )); + } + }; + + +/***/ }, +/* 170 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var coordsOffsetMap = { + '南海诸岛' : [32, 80], + // 全国 + '广东': [0, -10], + '香港': [10, 5], + '澳门': [-10, 10], + //'北京': [-10, 0], + '天津': [5, 5] + }; + + module.exports = function (geo) { + zrUtil.each(geo.regions, function (region) { + var coordFix = coordsOffsetMap[region.name]; + if (coordFix) { + var cp = region.center; + cp[0] += coordFix[0] / 10.5; + cp[1] += -coordFix[1] / (10.5 / 0.75); + } + }); + }; + + +/***/ }, +/* 171 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var geoCoordMap = { + 'Russia': [100, 60], + 'United States of America': [-99, 38] + }; + + module.exports = function (geo) { + zrUtil.each(geo.regions, function (region) { + var geoCoord = geoCoordMap[region.name]; + if (geoCoord) { + var cp = region.center; + cp[0] = geoCoord[0]; + cp[1] = geoCoord[1]; + } + }); + }; + + +/***/ }, +/* 172 */ +/***/ function(module, exports, __webpack_require__) { + + + + // var zrUtil = require('zrender/lib/core/util'); + var graphic = __webpack_require__(43); + + var MapDraw = __webpack_require__(173); + + __webpack_require__(1).extendChartView({ + + type: 'map', + + render: function (mapModel, ecModel, api, payload) { + // Not render if it is an toggleSelect action from self + if (payload && payload.type === 'mapToggleSelect' + && payload.from === this.uid + ) { + return; + } + + var group = this.group; + group.removeAll(); + // Not update map if it is an roam action from self + if (!(payload && payload.type === 'geoRoam' + && payload.componentType === 'series' + && payload.seriesId === mapModel.id)) { + + if (mapModel.needsDrawMap) { + var mapDraw = this._mapDraw || new MapDraw(api, true); + group.add(mapDraw.group); + + mapDraw.draw(mapModel, ecModel, api, this, payload); + + this._mapDraw = mapDraw; + } + else { + // Remove drawed map + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + } + } + else { + var mapDraw = this._mapDraw; + mapDraw && group.add(mapDraw.group); + } + + mapModel.get('showLegendSymbol') && ecModel.getComponent('legend') + && this._renderSymbols(mapModel, ecModel, api); + }, + + remove: function () { + this._mapDraw && this._mapDraw.remove(); + this._mapDraw = null; + this.group.removeAll(); + }, + + _renderSymbols: function (mapModel, ecModel, api) { + var originalData = mapModel.originalData; + var group = this.group; + + originalData.each('value', function (value, idx) { + if (isNaN(value)) { + return; + } + + var layout = originalData.getItemLayout(idx); + + if (!layout || !layout.point) { + // Not exists in map + return; + } + + var point = layout.point; + var offset = layout.offset; + + var circle = new graphic.Circle({ + style: { + // Because the special of map draw. + // Which needs statistic of multiple series and draw on one map. + // And each series also need a symbol with legend color + // + // Layout and visual are put one the different data + fill: mapModel.getData().getVisual('color') + }, + shape: { + cx: point[0] + offset * 9, + cy: point[1], + r: 3 + }, + silent: true, + z2: 10 + }); + + // First data on the same region + if (!offset) { + var fullData = mapModel.mainSeries.getData(); + var name = originalData.getName(idx); + var labelText = name; + var fullIndex = fullData.indexOfName(name); + + var itemModel = originalData.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var hoverLabelModel = itemModel.getModel('label.emphasis'); + + var textStyleModel = labelModel.getModel('textStyle'); + var hoverTextStyleModel = hoverLabelModel.getModel('textStyle'); + + var polygonGroups = fullData.getItemGraphicEl(fullIndex); + circle.setStyle({ + textPosition: 'bottom' + }); + + var onEmphasis = function () { + circle.setStyle({ + text: hoverLabelModel.get('show') ? labelText : '', + textFill: hoverTextStyleModel.getTextColor(), + textFont: hoverTextStyleModel.getFont() + }); + }; + + var onNormal = function () { + circle.setStyle({ + text: labelModel.get('show') ? labelText : '', + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + }); + }; + + polygonGroups.on('mouseover', onEmphasis) + .on('mouseout', onNormal) + .on('emphasis', onEmphasis) + .on('normal', onNormal); + + onNormal(); + } + + group.add(circle); + }); + } + }); + + +/***/ }, +/* 173 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/helper/MapDraw + */ + + + var RoamController = __webpack_require__(174); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + function getFixedItemStyle(model, scale) { + var itemStyle = model.getItemStyle(); + var areaColor = model.get('areaColor'); + if (areaColor) { + itemStyle.fill = areaColor; + } + + return itemStyle; + } + + function updateMapSelectHandler(mapOrGeoModel, group, api, fromView) { + group.off('click'); + mapOrGeoModel.get('selectedMode') + && group.on('click', function (e) { + var el = e.target; + while (!el.__region) { + el = el.parent; + } + if (!el) { + return; + } + + var region = el.__region; + var action = { + type: (mapOrGeoModel.mainType === 'geo' ? 'geo' : 'map') + 'ToggleSelect', + name: region.name, + from: fromView.uid + }; + action[mapOrGeoModel.mainType + 'Id'] = mapOrGeoModel.id; + + api.dispatchAction(action); + + updateMapSelected(mapOrGeoModel, group); + }); + } + + function updateMapSelected(mapOrGeoModel, group) { + // FIXME + group.eachChild(function (otherRegionEl) { + if (otherRegionEl.__region) { + otherRegionEl.trigger(mapOrGeoModel.isSelected(otherRegionEl.__region.name) ? 'emphasis' : 'normal'); + } + }); + } + + /** + * @alias module:echarts/component/helper/MapDraw + * @param {module:echarts/ExtensionAPI} api + * @param {boolean} updateGroup + */ + function MapDraw(api, updateGroup) { + + var group = new graphic.Group(); + + /** + * @type {module:echarts/component/helper/RoamController} + * @private + */ + this._controller = new RoamController( + api.getZr(), updateGroup ? group : null, null + ); + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = group; + + /** + * @type {boolean} + * @private + */ + this._updateGroup = updateGroup; + } + + MapDraw.prototype = { + + constructor: MapDraw, + + draw: function (mapOrGeoModel, ecModel, api, fromView, payload) { + + // geoModel has no data + var data = mapOrGeoModel.getData && mapOrGeoModel.getData(); + + var geo = mapOrGeoModel.coordinateSystem; + + var group = this.group; + + var scale = geo.scale; + var groupNewProp = { + position: geo.position, + scale: scale + }; + + // No animation when first draw or in action + if (!group.childAt(0) || payload) { + group.attr(groupNewProp); + } + else { + graphic.updateProps(group, groupNewProp, mapOrGeoModel); + } + + group.removeAll(); + + var itemStyleAccessPath = ['itemStyle', 'normal']; + var hoverItemStyleAccessPath = ['itemStyle', 'emphasis']; + var labelAccessPath = ['label', 'normal']; + var hoverLabelAccessPath = ['label', 'emphasis']; + + zrUtil.each(geo.regions, function (region) { + + var regionGroup = new graphic.Group(); + var compoundPath = new graphic.CompoundPath({ + shape: { + paths: [] + } + }); + regionGroup.add(compoundPath); + + var regionModel = mapOrGeoModel.getRegionModel(region.name) || mapOrGeoModel; + + var itemStyleModel = regionModel.getModel(itemStyleAccessPath); + var hoverItemStyleModel = regionModel.getModel(hoverItemStyleAccessPath); + var itemStyle = getFixedItemStyle(itemStyleModel, scale); + var hoverItemStyle = getFixedItemStyle(hoverItemStyleModel, scale); + + var labelModel = regionModel.getModel(labelAccessPath); + var hoverLabelModel = regionModel.getModel(hoverLabelAccessPath); + + var dataIdx; + // Use the itemStyle in data if has data + if (data) { + dataIdx = data.indexOfName(region.name); + // Only visual color of each item will be used. It can be encoded by dataRange + // But visual color of series is used in symbol drawing + // + // Visual color for each series is for the symbol draw + var visualColor = data.getItemVisual(dataIdx, 'color', true); + if (visualColor) { + itemStyle.fill = visualColor; + } + } + + var textStyleModel = labelModel.getModel('textStyle'); + var hoverTextStyleModel = hoverLabelModel.getModel('textStyle'); + + zrUtil.each(region.contours, function (contour) { + + var polygon = new graphic.Polygon({ + shape: { + points: contour + } + }); + + compoundPath.shape.paths.push(polygon); + }); + + compoundPath.setStyle(itemStyle); + compoundPath.style.strokeNoScale = true; + compoundPath.culling = true; + // Label + var showLabel = labelModel.get('show'); + var hoverShowLabel = hoverLabelModel.get('show'); + + var isDataNaN = data && isNaN(data.get('value', dataIdx)); + var itemLayout = data && data.getItemLayout(dataIdx); + // In the following cases label will be drawn + // 1. In map series and data value is NaN + // 2. In geo component + // 4. Region has no series legendSymbol, which will be add a showLabel flag in mapSymbolLayout + if ( + (!data || isDataNaN && (showLabel || hoverShowLabel)) + || (itemLayout && itemLayout.showLabel) + ) { + var query = data ? dataIdx : region.name; + var formattedStr = mapOrGeoModel.getFormattedLabel(query, 'normal'); + var hoverFormattedStr = mapOrGeoModel.getFormattedLabel(query, 'emphasis'); + var text = new graphic.Text({ + style: { + text: showLabel ? (formattedStr || region.name) : '', + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textAlign: 'center', + textVerticalAlign: 'middle' + }, + hoverStyle: { + text: hoverShowLabel ? (hoverFormattedStr || region.name) : '', + fill: hoverTextStyleModel.getTextColor(), + textFont: hoverTextStyleModel.getFont() + }, + position: region.center.slice(), + scale: [1 / scale[0], 1 / scale[1]], + z2: 10, + silent: true + }); + + regionGroup.add(text); + } + + // setItemGraphicEl, setHoverStyle after all polygons and labels + // are added to the rigionGroup + if (data) { + data.setItemGraphicEl(dataIdx, regionGroup); + } + else { + var regionModel = mapOrGeoModel.getRegionModel(region.name); + // Package custom mouse event for geo component + compoundPath.eventData = { + componentType: 'geo', + geoIndex: mapOrGeoModel.componentIndex, + name: region.name, + region: (regionModel && regionModel.option) || {} + }; + } + + regionGroup.__region = region; + + graphic.setHoverStyle(regionGroup, hoverItemStyle); + + group.add(regionGroup); + }); + + this._updateController(mapOrGeoModel, ecModel, api); + + updateMapSelectHandler(mapOrGeoModel, group, api, fromView); + + updateMapSelected(mapOrGeoModel, group); + }, + + remove: function () { + this.group.removeAll(); + this._controller.dispose(); + }, + + _updateController: function (mapOrGeoModel, ecModel, api) { + var geo = mapOrGeoModel.coordinateSystem; + var controller = this._controller; + controller.zoomLimit = mapOrGeoModel.get('scaleLimit'); + // Update zoom from model + controller.zoom = geo.getZoom(); + // roamType is will be set default true if it is null + controller.enable(mapOrGeoModel.get('roam') || false); + var mainType = mapOrGeoModel.mainType; + + function makeActionBase() { + var action = { + type: 'geoRoam', + componentType: mainType + }; + action[mainType + 'Id'] = mapOrGeoModel.id; + return action; + } + controller.off('pan') + .on('pan', function (dx, dy) { + api.dispatchAction(zrUtil.extend(makeActionBase(), { + dx: dx, + dy: dy + })); + }); + controller.off('zoom') + .on('zoom', function (zoom, mouseX, mouseY) { + api.dispatchAction(zrUtil.extend(makeActionBase(), { + zoom: zoom, + originX: mouseX, + originY: mouseY + })); + + if (this._updateGroup) { + var group = this.group; + var scale = group.scale; + group.traverse(function (el) { + if (el.type === 'text') { + el.attr('scale', [1 / scale[0], 1 / scale[1]]); + } + }); + } + }, this); + + controller.rectProvider = function () { + return geo.getViewRectAfterRoam(); + }; + } + }; + + module.exports = MapDraw; + + +/***/ }, +/* 174 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/helper/RoamController + */ + + + + var Eventful = __webpack_require__(33); + var zrUtil = __webpack_require__(4); + var eventTool = __webpack_require__(87); + var interactionMutex = __webpack_require__(175); + + function mousedown(e) { + if (e.target && e.target.draggable) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + var rect = this.rectProvider && this.rectProvider(); + if (rect && rect.contain(x, y)) { + this._x = x; + this._y = y; + this._dragging = true; + } + } + + function mousemove(e) { + if (!this._dragging) { + return; + } + + eventTool.stop(e.event); + + if (e.gestureEvent !== 'pinch') { + + if (interactionMutex.isTaken(this._zr, 'globalPan')) { + return; + } + + var x = e.offsetX; + var y = e.offsetY; + + var dx = x - this._x; + var dy = y - this._y; + + this._x = x; + this._y = y; + + var target = this.target; + + if (target) { + var pos = target.position; + pos[0] += dx; + pos[1] += dy; + target.dirty(); + } + + eventTool.stop(e.event); + this.trigger('pan', dx, dy); + } + } + + function mouseup(e) { + this._dragging = false; + } + + function mousewheel(e) { + // Convenience: + // Mac and VM Windows on Mac: scroll up: zoom out. + // Windows: scroll up: zoom in. + var zoomDelta = e.wheelDelta > 0 ? 1.1 : 1 / 1.1; + zoom.call(this, e, zoomDelta, e.offsetX, e.offsetY); + } + + function pinch(e) { + if (interactionMutex.isTaken(this._zr, 'globalPan')) { + return; + } + var zoomDelta = e.pinchScale > 1 ? 1.1 : 1 / 1.1; + zoom.call(this, e, zoomDelta, e.pinchX, e.pinchY); + } + + function zoom(e, zoomDelta, zoomX, zoomY) { + var rect = this.rectProvider && this.rectProvider(); + + if (rect && rect.contain(zoomX, zoomY)) { + // When mouse is out of roamController rect, + // default befavoius should be be disabled, otherwise + // page sliding is disabled, contrary to expectation. + eventTool.stop(e.event); + + var target = this.target; + var zoomLimit = this.zoomLimit; + + if (target) { + var pos = target.position; + var scale = target.scale; + + var newZoom = this.zoom = this.zoom || 1; + newZoom *= zoomDelta; + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + newZoom = Math.max( + Math.min(zoomMax, newZoom), + zoomMin + ); + } + var zoomScale = newZoom / this.zoom; + this.zoom = newZoom; + // Keep the mouse center when scaling + pos[0] -= (zoomX - pos[0]) * (zoomScale - 1); + pos[1] -= (zoomY - pos[1]) * (zoomScale - 1); + scale[0] *= zoomScale; + scale[1] *= zoomScale; + + target.dirty(); + } + + this.trigger('zoom', zoomDelta, zoomX, zoomY); + } + } + + /** + * @alias module:echarts/component/helper/RoamController + * @constructor + * @mixin {module:zrender/mixin/Eventful} + * + * @param {module:zrender/zrender~ZRender} zr + * @param {module:zrender/Element} target + * @param {Function} [rectProvider] + */ + function RoamController(zr, target, rectProvider) { + + /** + * @type {module:zrender/Element} + */ + this.target = target; + + /** + * @type {Function} + */ + this.rectProvider = rectProvider; + + /** + * { min: 1, max: 2 } + * @type {Object} + */ + this.zoomLimit; + + /** + * @type {number} + */ + this.zoom; + /** + * @type {module:zrender} + */ + this._zr = zr; + + // Avoid two roamController bind the same handler + var bind = zrUtil.bind; + var mousedownHandler = bind(mousedown, this); + var mousemoveHandler = bind(mousemove, this); + var mouseupHandler = bind(mouseup, this); + var mousewheelHandler = bind(mousewheel, this); + var pinchHandler = bind(pinch, this); + + Eventful.call(this); + + /** + * Notice: only enable needed types. For example, if 'zoom' + * is not needed, 'zoom' should not be enabled, otherwise + * default mousewheel behaviour (scroll page) will be disabled. + * + * @param {boolean|string} [controlType=true] Specify the control type, + * which can be null/undefined or true/false + * or 'pan/move' or 'zoom'/'scale' + */ + this.enable = function (controlType) { + // Disable previous first + this.disable(); + + if (controlType == null) { + controlType = true; + } + + if (controlType === true || (controlType === 'move' || controlType === 'pan')) { + zr.on('mousedown', mousedownHandler); + zr.on('mousemove', mousemoveHandler); + zr.on('mouseup', mouseupHandler); + } + if (controlType === true || (controlType === 'scale' || controlType === 'zoom')) { + zr.on('mousewheel', mousewheelHandler); + zr.on('pinch', pinchHandler); + } + }; + + this.disable = function () { + zr.off('mousedown', mousedownHandler); + zr.off('mousemove', mousemoveHandler); + zr.off('mouseup', mouseupHandler); + zr.off('mousewheel', mousewheelHandler); + zr.off('pinch', pinchHandler); + }; + + this.dispose = this.disable; + + this.isDragging = function () { + return this._dragging; + }; + + this.isPinching = function () { + return this._pinching; + }; + } + + zrUtil.mixin(RoamController, Eventful); + + module.exports = RoamController; + + +/***/ }, +/* 175 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ATTR = '\0_ec_interaction_mutex'; + + var interactionMutex = { + + take: function (zr, resourceKey, userKey) { + var store = getStore(zr); + store[resourceKey] = userKey; + }, + + release: function (zr, resourceKey, userKey) { + var store = getStore(zr); + var uKey = store[resourceKey]; + + if (uKey === userKey) { + store[resourceKey] = null; + } + }, + + isTaken: function (zr, resourceKey) { + return !!getStore(zr)[resourceKey]; + } + }; + + function getStore(zr) { + return zr[ATTR] || (zr[ATTR] = {}); + } + + /** + * payload: { + * type: 'takeGlobalCursor', + * key: 'dataZoomSelect', or 'brush', or ..., + * If no userKey, release global cursor. + * } + */ + __webpack_require__(1).registerAction( + {type: 'takeGlobalCursor', event: 'globalCursorTaken', update: 'update'}, + function () {} + ); + + module.exports = interactionMutex; + + +/***/ }, +/* 176 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var roamHelper = __webpack_require__(177); + + var echarts = __webpack_require__(1); + + /** + * @payload + * @property {string} [componentType=series] + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + echarts.registerAction({ + type: 'geoRoam', + event: 'geoRoam', + update: 'updateLayout' + }, function (payload, ecModel) { + var componentType = payload.componentType || 'series'; + + ecModel.eachComponent( + { mainType: componentType, query: payload }, + function (componentModel) { + var geo = componentModel.coordinateSystem; + if (geo.type !== 'geo') { + return; + } + + var res = roamHelper.updateCenterAndZoom( + geo, payload, componentModel.get('scaleLimit') + ); + + componentModel.setCenter + && componentModel.setCenter(res.center); + + componentModel.setZoom + && componentModel.setZoom(res.zoom); + + // All map series with same `map` use the same geo coordinate system + // So the center and zoom must be in sync. Include the series not selected by legend + if (componentType === 'series') { + zrUtil.each(componentModel.seriesGroup, function (seriesModel) { + seriesModel.setCenter(res.center); + seriesModel.setZoom(res.zoom); + }); + } + } + ); + }); + + +/***/ }, +/* 177 */ +/***/ function(module, exports) { + + + + var roamHelper = {}; + + /** + * @param {module:echarts/coord/View} view + * @param {Object} payload + * @param {Object} [zoomLimit] + */ + roamHelper.updateCenterAndZoom = function ( + view, payload, zoomLimit + ) { + var previousZoom = view.getZoom(); + var center = view.getCenter(); + var zoom = payload.zoom; + + var point = view.dataToPoint(center); + + if (payload.dx != null && payload.dy != null) { + point[0] -= payload.dx; + point[1] -= payload.dy; + + var center = view.pointToData(point); + view.setCenter(center); + } + if (zoom != null) { + if (zoomLimit) { + var zoomMin = zoomLimit.min || 0; + var zoomMax = zoomLimit.max || Infinity; + zoom = Math.max( + Math.min(previousZoom * zoom, zoomMax), + zoomMin + ) / previousZoom; + } + + // Zoom on given point(originX, originY) + view.scale[0] *= zoom; + view.scale[1] *= zoom; + var position = view.position; + var fixX = (payload.originX - position[0]) * (zoom - 1); + var fixY = (payload.originY - position[1]) * (zoom - 1); + + position[0] -= fixX; + position[1] -= fixY; + + view.updateTransform(); + // Get the new center + var center = view.pointToData(point); + view.setCenter(center); + view.setZoom(zoom * previousZoom); + } + + return { + center: view.getCenter(), + zoom: view.getZoom() + }; + }; + + module.exports = roamHelper; + + +/***/ }, +/* 178 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel) { + + var processedMapType = {}; + + ecModel.eachSeriesByType('map', function (mapSeries) { + var mapType = mapSeries.get('map'); + if (processedMapType[mapType]) { + return; + } + + var mapSymbolOffsets = {}; + + zrUtil.each(mapSeries.seriesGroup, function (subMapSeries) { + var geo = subMapSeries.coordinateSystem; + var data = subMapSeries.originalData; + if (subMapSeries.get('showLegendSymbol') && ecModel.getComponent('legend')) { + data.each('value', function (value, idx) { + var name = data.getName(idx); + var region = geo.getRegion(name); + + // No region or no value + // In MapSeries data regions will be filled with NaN + // If they are not in the series.data array. + // So here must validate if value is NaN + if (!region || isNaN(value)) { + return; + } + + var offset = mapSymbolOffsets[name] || 0; + + var point = geo.dataToPoint(region.center); + + mapSymbolOffsets[name] = offset + 1; + + data.setItemLayout(idx, { + point: point, + offset: offset + }); + }); + } + }); + + // Show label of those region not has legendSymbol(which is offset 0) + var data = mapSeries.getData(); + data.each(function (idx) { + var name = data.getName(idx); + var layout = data.getItemLayout(idx) || {}; + layout.showLabel = !mapSymbolOffsets[name]; + data.setItemLayout(idx, layout); + }); + + processedMapType[mapType] = true; + }); + }; + + +/***/ }, +/* 179 */ +/***/ function(module, exports) { + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('map', function (seriesModel) { + var colorList = seriesModel.get('color'); + var itemStyleModel = seriesModel.getModel('itemStyle.normal'); + + var areaColor = itemStyleModel.get('areaColor'); + var color = itemStyleModel.get('color') + || colorList[seriesModel.seriesIndex % colorList.length]; + + seriesModel.getData().setVisual({ + 'areaColor': areaColor, + 'color': color + }); + }); + }; + + +/***/ }, +/* 180 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + // FIXME 公用? + /** + * @param {Array.} datas + * @param {string} statisticType 'average' 'sum' + * @inner + */ + function dataStatistics(datas, statisticType) { + var dataNameMap = {}; + var dims = ['value']; + + for (var i = 0; i < datas.length; i++) { + datas[i].each(dims, function (value, idx) { + var name = datas[i].getName(idx); + dataNameMap[name] = dataNameMap[name] || []; + if (!isNaN(value)) { + dataNameMap[name].push(value); + } + }); + } + + return datas[0].map(dims, function (value, idx) { + var name = datas[0].getName(idx); + var sum = 0; + var min = Infinity; + var max = -Infinity; + var len = dataNameMap[name].length; + for (var i = 0; i < len; i++) { + min = Math.min(min, dataNameMap[name][i]); + max = Math.max(max, dataNameMap[name][i]); + sum += dataNameMap[name][i]; + } + var result; + if (statisticType === 'min') { + result = min; + } + else if (statisticType === 'max') { + result = max; + } + else if (statisticType === 'average') { + result = sum / len; + } + else { + result = sum; + } + return len === 0 ? NaN : result; + }); + } + + module.exports = function (ecModel) { + var seriesGroupByMapType = {}; + ecModel.eachSeriesByType('map', function (seriesModel) { + var mapType = seriesModel.get('map'); + seriesGroupByMapType[mapType] = seriesGroupByMapType[mapType] || []; + seriesGroupByMapType[mapType].push(seriesModel); + }); + + zrUtil.each(seriesGroupByMapType, function (seriesList, mapType) { + var data = dataStatistics( + zrUtil.map(seriesList, function (seriesModel) { + return seriesModel.getData(); + }), + seriesList[0].get('mapValueCalculation') + ); + + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].originalData = seriesList[i].getData(); + } + + // FIXME Put where? + for (var i = 0; i < seriesList.length; i++) { + seriesList[i].seriesGroup = seriesList; + seriesList[i].needsDrawMap = i === 0; + + seriesList[i].setData(data.cloneShallow()); + seriesList[i].mainSeries = seriesList[0]; + } + }); + }; + + +/***/ }, +/* 181 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + // Save geoCoord + var mapSeries = []; + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt.type === 'map') { + mapSeries.push(seriesOpt); + } + }); + + zrUtil.each(mapSeries, function (seriesOpt) { + seriesOpt.map = seriesOpt.map || seriesOpt.mapType; + // Put x, y, width, height, x2, y2 in the top level + zrUtil.defaults(seriesOpt, seriesOpt.mapLocation); + }); + }; + + +/***/ }, +/* 182 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(183); + __webpack_require__(186); + __webpack_require__(190); + + echarts.registerVisual(__webpack_require__(191)); + + echarts.registerLayout(__webpack_require__(193)); + + +/***/ }, +/* 183 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SeriesModel = __webpack_require__(28); + var Tree = __webpack_require__(184); + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + + module.exports = SeriesModel.extend({ + + type: 'series.treemap', + + dependencies: ['grid', 'polar'], + + /** + * @type {module:echarts/data/Tree~Node} + */ + _viewRoot: null, + + defaultOption: { + // Disable progressive rendering + progressive: 0, + hoverLayerThreshold: Infinity, + // center: ['50%', '50%'], // not supported in ec3. + // size: ['80%', '80%'], // deprecated, compatible with ec2. + left: 'center', + top: 'middle', + right: null, + bottom: null, + width: '80%', + height: '80%', + sort: true, // Can be null or false or true + // (order by desc default, asc not supported yet (strange effect)) + clipWindow: 'origin', // Size of clipped window when zooming. 'origin' or 'fullscreen' + squareRatio: 0.5 * (1 + Math.sqrt(5)), // golden ratio + leafDepth: null, // Nodes on depth from root are regarded as leaves. + // Count from zero (zero represents only view root). + drillDownIcon: '▶', // Use html character temporarily because it is complicated + // to align specialized icon. ▷▶❒❐▼✚ + visualDimension: 0, // Can be 0, 1, 2, 3. + zoomToNodeRatio: 0.32 * 0.32, // Be effective when using zoomToNode. Specify the proportion of the + // target node area in the view area. + roam: true, // true, false, 'scale' or 'zoom', 'move'. + nodeClick: 'zoomToNode', // Leaf node click behaviour: 'zoomToNode', 'link', false. + // If leafDepth is set and clicking a node which has children but + // be on left depth, the behaviour would be changing root. Otherwise + // use behavious defined above. + animation: true, + animationDurationUpdate: 900, + animationEasing: 'quinticInOut', + breadcrumb: { + show: true, + height: 22, + left: 'center', + top: 'bottom', + // right + // bottom + emptyItemWidth: 25, // Width of empty node. + itemStyle: { + normal: { + color: 'rgba(0,0,0,0.7)', //'#5793f3', + borderColor: 'rgba(255,255,255,0.7)', + borderWidth: 1, + shadowColor: 'rgba(150,150,150,1)', + shadowBlur: 3, + shadowOffsetX: 0, + shadowOffsetY: 0, + textStyle: { + color: '#fff' + } + }, + emphasis: { + textStyle: {} + } + } + }, + label: { + normal: { + show: true, + position: 'inside', // Can be [5, '5%'] or position stirng like 'insideTopLeft', ... + textStyle: { + color: '#fff', + ellipsis: true + } + } + }, + itemStyle: { + normal: { + color: null, // Can be 'none' if not necessary. + colorAlpha: null, // Can be 'none' if not necessary. + colorSaturation: null, // Can be 'none' if not necessary. + borderWidth: 0, + gapWidth: 0, + borderColor: '#fff', + borderColorSaturation: null // If specified, borderColor will be ineffective, and the + // border color is evaluated by color of current node and + // borderColorSaturation. + }, + emphasis: { + + } + }, + color: [], // + treemapSeries.color should not be modified. Please only modified + // level[n].color (if necessary). + // + Specify color list of each level. level[0].color would be global + // color list if not specified. (see method `setDefault`). + // + But set as a empty array to forbid fetch color from global palette + // when using nodeModel.get('color'), otherwise nodes on deep level + // will always has color palette set and are not able to inherit color + // from parent node. + // + TreemapSeries.color can not be set as 'none', otherwise effect + // legend color fetching (see seriesColor.js). + colorAlpha: null, // Array. Specify color alpha range of each level, like [0.2, 0.8] + colorSaturation: null, // Array. Specify color saturation of each level, like [0.2, 0.5] + colorMappingBy: 'index', // 'value' or 'index' or 'id'. + visibleMin: 10, // If area less than this threshold (unit: pixel^2), node will not + // be rendered. Only works when sort is 'asc' or 'desc'. + childrenVisibleMin: null, // If area of a node less than this threshold (unit: pixel^2), + // grandchildren will not show. + // Why grandchildren? If not grandchildren but children, + // some siblings show children and some not, + // the appearance may be mess and not consistent, + levels: [] // Each item: { + // visibleMin, itemStyle, visualDimension, label + // } + // data: { + // value: [], + // children: [], + // link: 'http://xxx.xxx.xxx', + // target: 'blank' or 'self' + // } + }, + + /** + * @override + */ + getInitialData: function (option, ecModel) { + var data = option.data || []; + var rootName = option.name; + rootName == null && (rootName = option.name); + + // Create a virtual root. + var root = {name: rootName, children: option.data}; + var value0 = (data[0] || {}).value; + + completeTreeValue(root, zrUtil.isArray(value0) ? value0.length : -1); + + // FIXME + // sereis.mergeOption 的 getInitData是否放在merge后,从而能直接获取merege后的结果而非手动判断。 + var levels = option.levels || []; + + levels = option.levels = setDefault(levels, ecModel); + + // Make sure always a new tree is created when setOption, + // in TreemapView, we check whether oldTree === newTree + // to choose mappings approach among old shapes and new shapes. + return Tree.createTree(root, this, levels).data; + }, + + optionUpdated: function () { + this.resetViewRoot(); + }, + + /** + * @override + * @param {number} dataIndex + * @param {boolean} [mutipleSeries=false] + */ + formatTooltip: function (dataIndex) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? addCommas(value[0]) : addCommas(value); + var name = data.getName(dataIndex); + + return encodeHTML(name) + ': ' + formattedValue; + }, + + /** + * Add tree path to tooltip param + * + * @override + * @param {number} dataIndex + * @return {Object} + */ + getDataParams: function (dataIndex) { + var params = SeriesModel.prototype.getDataParams.apply(this, arguments); + + var data = this.getData(); + var node = data.tree.getNodeByDataIndex(dataIndex); + var treePathInfo = params.treePathInfo = []; + + while (node) { + var nodeDataIndex = node.dataIndex; + treePathInfo.push({ + name: node.name, + dataIndex: nodeDataIndex, + value: this.getRawValue(nodeDataIndex) + }); + node = node.parentNode; + } + + treePathInfo.reverse(); + + return params; + }, + + /** + * @public + * @param {Object} layoutInfo { + * x: containerGroup x + * y: containerGroup y + * width: containerGroup width + * height: containerGroup height + * } + */ + setLayoutInfo: function (layoutInfo) { + /** + * @readOnly + * @type {Object} + */ + this.layoutInfo = this.layoutInfo || {}; + zrUtil.extend(this.layoutInfo, layoutInfo); + }, + + /** + * @param {string} id + * @return {number} index + */ + mapIdToIndex: function (id) { + // A feature is implemented: + // index is monotone increasing with the sequence of + // input id at the first time. + // This feature can make sure that each data item and its + // mapped color have the same index between data list and + // color list at the beginning, which is useful for user + // to adjust data-color mapping. + + /** + * @private + * @type {Object} + */ + var idIndexMap = this._idIndexMap; + + if (!idIndexMap) { + idIndexMap = this._idIndexMap = {}; + /** + * @private + * @type {number} + */ + this._idIndexMapCount = 0; + } + + var index = idIndexMap[id]; + if (index == null) { + idIndexMap[id] = index = this._idIndexMapCount++; + } + + return index; + }, + + getViewRoot: function () { + return this._viewRoot; + }, + + /** + * @param {module:echarts/data/Tree~Node} [viewRoot] + */ + resetViewRoot: function (viewRoot) { + viewRoot + ? (this._viewRoot = viewRoot) + : (viewRoot = this._viewRoot); + + var root = this.getData().tree.root; + + if (!viewRoot + || (viewRoot !== root && !root.contains(viewRoot)) + ) { + this._viewRoot = root; + } + } + }); + + /** + * @param {Object} dataNode + */ + function completeTreeValue(dataNode, arrValueLength) { + // Postorder travel tree. + // If value of none-leaf node is not set, + // calculate it by suming up the value of all children. + var sum = 0; + + zrUtil.each(dataNode.children, function (child) { + + completeTreeValue(child, arrValueLength); + + var childValue = child.value; + zrUtil.isArray(childValue) && (childValue = childValue[0]); + + sum += childValue; + }); + + var thisValue = dataNode.value; + + if (arrValueLength >= 0) { + if (!zrUtil.isArray(thisValue)) { + dataNode.value = new Array(arrValueLength); + } + else { + thisValue = thisValue[0]; + } + } + + if (thisValue == null || isNaN(thisValue)) { + thisValue = sum; + } + // Value should not less than 0. + if (thisValue < 0) { + thisValue = 0; + } + + arrValueLength >= 0 + ? (dataNode.value[0] = thisValue) + : (dataNode.value = thisValue); + } + + /** + * set default to level configuration + */ + function setDefault(levels, ecModel) { + var globalColorList = ecModel.get('color'); + + if (!globalColorList) { + return; + } + + levels = levels || []; + var hasColorDefine; + zrUtil.each(levels, function (levelDefine) { + var model = new Model(levelDefine); + var modelColor = model.get('color'); + + if (model.get('itemStyle.normal.color') + || (modelColor && modelColor !== 'none') + ) { + hasColorDefine = true; + } + }); + + if (!hasColorDefine) { + var level0 = levels[0] || (levels[0] = {}); + level0.color = globalColorList.slice(); + } + + return levels; + } + + + +/***/ }, +/* 184 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Tree data structure + * + * @module echarts/data/Tree + */ + + + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + var List = __webpack_require__(97); + var linkList = __webpack_require__(185); + var completeDimensions = __webpack_require__(102); + + /** + * @constructor module:echarts/data/Tree~TreeNode + * @param {string} name + * @param {module:echarts/data/Tree} hostTree + */ + var TreeNode = function (name, hostTree) { + /** + * @type {string} + */ + this.name = name || ''; + + /** + * Depth of node + * + * @type {number} + * @readOnly + */ + this.depth = 0; + + /** + * Height of the subtree rooted at this node. + * @type {number} + * @readOnly + */ + this.height = 0; + + /** + * @type {module:echarts/data/Tree~TreeNode} + * @readOnly + */ + this.parentNode = null; + + /** + * Reference to list item. + * Do not persistent dataIndex outside, + * besause it may be changed by list. + * If dataIndex -1, + * this node is logical deleted (filtered) in list. + * + * @type {Object} + * @readOnly + */ + this.dataIndex = -1; + + /** + * @type {Array.} + * @readOnly + */ + this.children = []; + + /** + * @type {Array.} + * @pubilc + */ + this.viewChildren = []; + + /** + * @type {moduel:echarts/data/Tree} + * @readOnly + */ + this.hostTree = hostTree; + }; + + TreeNode.prototype = { + + constructor: TreeNode, + + /** + * The node is removed. + * @return {boolean} is removed. + */ + isRemoved: function () { + return this.dataIndex < 0; + }, + + /** + * Travel this subtree (include this node). + * Usage: + * node.eachNode(function () { ... }); // preorder + * node.eachNode('preorder', function () { ... }); // preorder + * node.eachNode('postorder', function () { ... }); // postorder + * node.eachNode( + * {order: 'postorder', attr: 'viewChildren'}, + * function () { ... } + * ); // postorder + * + * @param {(Object|string)} options If string, means order. + * @param {string=} options.order 'preorder' or 'postorder' + * @param {string=} options.attr 'children' or 'viewChildren' + * @param {Function} cb If in preorder and return false, + * its subtree will not be visited. + * @param {Object} [context] + */ + eachNode: function (options, cb, context) { + if (typeof options === 'function') { + context = cb; + cb = options; + options = null; + } + + options = options || {}; + if (zrUtil.isString(options)) { + options = {order: options}; + } + + var order = options.order || 'preorder'; + var children = this[options.attr || 'children']; + + var suppressVisitSub; + order === 'preorder' && (suppressVisitSub = cb.call(context, this)); + + for (var i = 0; !suppressVisitSub && i < children.length; i++) { + children[i].eachNode(options, cb, context); + } + + order === 'postorder' && cb.call(context, this); + }, + + /** + * Update depth and height of this subtree. + * + * @param {number} depth + */ + updateDepthAndHeight: function (depth) { + var height = 0; + this.depth = depth; + for (var i = 0; i < this.children.length; i++) { + var child = this.children[i]; + child.updateDepthAndHeight(depth + 1); + if (child.height > height) { + height = child.height; + } + } + this.height = height + 1; + }, + + /** + * @param {string} id + * @return {module:echarts/data/Tree~TreeNode} + */ + getNodeById: function (id) { + if (this.getId() === id) { + return this; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].getNodeById(id); + if (res) { + return res; + } + } + }, + + /** + * @param {module:echarts/data/Tree~TreeNode} node + * @return {boolean} + */ + contains: function (node) { + if (node === this) { + return true; + } + for (var i = 0, children = this.children, len = children.length; i < len; i++) { + var res = children[i].contains(node); + if (res) { + return res; + } + } + }, + + /** + * @param {boolean} includeSelf Default false. + * @return {Array.} order: [root, child, grandchild, ...] + */ + getAncestors: function (includeSelf) { + var ancestors = []; + var node = includeSelf ? this : this.parentNode; + while (node) { + ancestors.push(node); + node = node.parentNode; + } + ancestors.reverse(); + return ancestors; + }, + + /** + * @param {string|Array=} [dimension='value'] Default 'value'. can be 0, 1, 2, 3 + * @return {number} Value. + */ + getValue: function (dimension) { + var data = this.hostTree.data; + return data.get(data.getDimension(dimension || 'value'), this.dataIndex); + }, + + /** + * @param {Object} layout + * @param {boolean=} [merge=false] + */ + setLayout: function (layout, merge) { + this.dataIndex >= 0 + && this.hostTree.data.setItemLayout(this.dataIndex, layout, merge); + }, + + /** + * @return {Object} layout + */ + getLayout: function () { + return this.hostTree.data.getItemLayout(this.dataIndex); + }, + + /** + * @param {string} path + * @return {module:echarts/model/Model} + */ + getModel: function (path) { + if (this.dataIndex < 0) { + return; + } + var hostTree = this.hostTree; + var itemModel = hostTree.data.getItemModel(this.dataIndex); + var levelModel = this.getLevelModel(); + + return itemModel.getModel(path, (levelModel || hostTree.hostModel).getModel(path)); + }, + + /** + * @return {module:echarts/model/Model} + */ + getLevelModel: function () { + return (this.hostTree.levelModels || [])[this.depth]; + }, + + /** + * @example + * setItemVisual('color', color); + * setItemVisual({ + * 'color': color + * }); + */ + setVisual: function (key, value) { + this.dataIndex >= 0 + && this.hostTree.data.setItemVisual(this.dataIndex, key, value); + }, + + /** + * Get item visual + */ + getVisual: function (key, ignoreParent) { + return this.hostTree.data.getItemVisual(this.dataIndex, key, ignoreParent); + }, + + /** + * @public + * @return {number} + */ + getRawIndex: function () { + return this.hostTree.data.getRawIndex(this.dataIndex); + }, + + /** + * @public + * @return {string} + */ + getId: function () { + return this.hostTree.data.getId(this.dataIndex); + } + }; + + /** + * @constructor + * @alias module:echarts/data/Tree + * @param {module:echarts/model/Model} hostModel + * @param {Array.} levelOptions + */ + function Tree(hostModel, levelOptions) { + /** + * @type {module:echarts/data/Tree~TreeNode} + * @readOnly + */ + this.root; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.data; + + /** + * Index of each item is the same as the raw index of coresponding list item. + * @private + * @type {Array.} levelOptions + * @return module:echarts/data/Tree + */ + Tree.createTree = function (dataRoot, hostModel, levelOptions) { + + var tree = new Tree(hostModel, levelOptions); + var listData = []; + + buildHierarchy(dataRoot); + + function buildHierarchy(dataNode, parentNode) { + listData.push(dataNode); + + var node = new TreeNode(dataNode.name, tree); + parentNode + ? addChild(node, parentNode) + : (tree.root = node); + + tree._nodes.push(node); + + var children = dataNode.children; + if (children) { + for (var i = 0; i < children.length; i++) { + buildHierarchy(children[i], node); + } + } + } + + tree.root.updateDepthAndHeight(0); + + var dimensions = completeDimensions([{name: 'value'}], listData); + var list = new List(dimensions, hostModel); + list.initData(listData); + + linkList({ + mainData: list, + struct: tree, + structAttr: 'tree' + }); + + tree.update(); + + return tree; + }; + + /** + * It is needed to consider the mess of 'list', 'hostModel' when creating a TreeNote, + * so this function is not ready and not necessary to be public. + * + * @param {(module:echarts/data/Tree~TreeNode|Object)} child + */ + function addChild(child, node) { + var children = node.children; + if (child.parentNode === node) { + return; + } + + children.push(child); + child.parentNode = node; + } + + module.exports = Tree; + + +/***/ }, +/* 185 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Link lists and struct (graph or tree) + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + var DATAS = '\0__link_datas'; + var MAIN_DATA = '\0__link_mainData'; + + // Caution: + // In most case, either list or its shallow clones (see list.cloneShallow) + // is active in echarts process. So considering heap memory consumption, + // we do not clone tree or graph, but share them among list and its shallow clones. + // But in some rare case, we have to keep old list (like do animation in chart). So + // please take care that both the old list and the new list share the same tree/graph. + + /** + * @param {Object} opt + * @param {module:echarts/data/List} opt.mainData + * @param {Object} [opt.struct] For example, instance of Graph or Tree. + * @param {string} [opt.structAttr] designation: list[structAttr] = struct; + * @param {Object} [opt.datas] {dataType: data}, + * like: {node: nodeList, edge: edgeList}. + * Should contain mainData. + * @param {Object} [opt.datasAttr] {dataType: attr}, + * designation: struct[datasAttr[dataType]] = list; + */ + function linkList(opt) { + var mainData = opt.mainData; + var datas = opt.datas; + + if (!datas) { + datas = {main: mainData}; + opt.datasAttr = {main: 'data'}; + } + opt.datas = opt.mainData = null; + + linkAll(mainData, datas, opt); + + // Porxy data original methods. + each(datas, function (data) { + each(mainData.TRANSFERABLE_METHODS, function (methodName) { + data.wrapMethod(methodName, zrUtil.curry(transferInjection, opt)); + }); + + }); + + // Beyond transfer, additional features should be added to `cloneShallow`. + mainData.wrapMethod('cloneShallow', zrUtil.curry(cloneShallowInjection, opt)); + + // Only mainData trigger change, because struct.update may trigger + // another changable methods, which may bring about dead lock. + each(mainData.CHANGABLE_METHODS, function (methodName) { + mainData.wrapMethod(methodName, zrUtil.curry(changeInjection, opt)); + }); + + // Make sure datas contains mainData. + zrUtil.assert(datas[mainData.dataType] === mainData); + } + + function transferInjection(opt, res) { + if (isMainData(this)) { + // Transfer datas to new main data. + var datas = zrUtil.extend({}, this[DATAS]); + datas[this.dataType] = res; + linkAll(res, datas, opt); + } + else { + // Modify the reference in main data to point newData. + linkSingle(res, this.dataType, this[MAIN_DATA], opt); + } + return res; + } + + function changeInjection(opt, res) { + opt.struct && opt.struct.update(this); + return res; + } + + function cloneShallowInjection(opt, res) { + // cloneShallow, which brings about some fragilities, may be inappropriate + // to be exposed as an API. So for implementation simplicity we can make + // the restriction that cloneShallow of not-mainData should not be invoked + // outside, but only be invoked here. + each(res[DATAS], function (data, dataType) { + data !== res && linkSingle(data.cloneShallow(), dataType, res, opt); + }); + return res; + } + + /** + * Supplement method to List. + * + * @public + * @param {string} [dataType] If not specified, return mainData. + * @return {module:echarts/data/List} + */ + function getLinkedData(dataType) { + var mainData = this[MAIN_DATA]; + return (dataType == null || mainData == null) + ? mainData + : mainData[DATAS][dataType]; + } + + function isMainData(data) { + return data[MAIN_DATA] === data; + } + + function linkAll(mainData, datas, opt) { + mainData[DATAS] = {}; + each(datas, function (data, dataType) { + linkSingle(data, dataType, mainData, opt); + }); + } + + function linkSingle(data, dataType, mainData, opt) { + mainData[DATAS][dataType] = data; + data[MAIN_DATA] = mainData; + data.dataType = dataType; + + if (opt.struct) { + data[opt.structAttr] = opt.struct; + opt.struct[opt.datasAttr[dataType]] = data; + } + + // Supplement method. + data.getLinkedData = getLinkedData; + } + + module.exports = linkList; + + +/***/ }, +/* 186 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var DataDiffer = __webpack_require__(98); + var helper = __webpack_require__(187); + var Breadcrumb = __webpack_require__(188); + var RoamController = __webpack_require__(174); + var BoundingRect = __webpack_require__(9); + var matrix = __webpack_require__(11); + var animationUtil = __webpack_require__(189); + var bind = zrUtil.bind; + var Group = graphic.Group; + var Rect = graphic.Rect; + var each = zrUtil.each; + + var DRAG_THRESHOLD = 3; + var PATH_LABEL_NORMAL = ['label', 'normal']; + var PATH_LABEL_EMPHASIS = ['label', 'emphasis']; + var Z_BASE = 10; // Should bigger than every z. + var Z_BG = 1; + var Z_CONTENT = 2; + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'treemap', + + /** + * @override + */ + init: function (o, api) { + + /** + * @private + * @type {module:zrender/container/Group} + */ + this._containerGroup; + + /** + * @private + * @type {Object.>} + */ + this._storage = createStorage(); + + /** + * @private + * @type {module:echarts/data/Tree} + */ + this._oldTree; + + /** + * @private + * @type {module:echarts/chart/treemap/Breadcrumb} + */ + this._breadcrumb; + + /** + * @private + * @type {module:echarts/component/helper/RoamController} + */ + this._controller; + + /** + * 'ready', 'animating' + * @private + */ + this._state = 'ready'; + + /** + * @private + * @type {boolean} + */ + this._mayClick; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + + var models = ecModel.findComponents({ + mainType: 'series', subType: 'treemap', query: payload + }); + if (zrUtil.indexOf(models, seriesModel) < 0) { + return; + } + + this.seriesModel = seriesModel; + this.api = api; + this.ecModel = ecModel; + + var targetInfo = helper.retrieveTargetInfo(payload, seriesModel); + var payloadType = payload && payload.type; + var layoutInfo = seriesModel.layoutInfo; + var isInit = !this._oldTree; + var thisStorage = this._storage; + + // Mark new root when action is treemapRootToNode. + var reRoot = (payloadType === 'treemapRootToNode' && targetInfo && thisStorage) + ? { + rootNodeGroup: thisStorage.nodeGroup[targetInfo.node.getRawIndex()], + direction: payload.direction + } + : null; + + var containerGroup = this._giveContainerGroup(layoutInfo); + + var renderResult = this._doRender(containerGroup, seriesModel, reRoot); + ( + !isInit && ( + !payloadType + || payloadType === 'treemapZoomToNode' + || payloadType === 'treemapRootToNode' + ) + ) + ? this._doAnimation(containerGroup, renderResult, seriesModel, reRoot) + : renderResult.renderFinally(); + + this._resetController(api); + + this._renderBreadcrumb(seriesModel, api, targetInfo); + }, + + /** + * @private + */ + _giveContainerGroup: function (layoutInfo) { + var containerGroup = this._containerGroup; + if (!containerGroup) { + // FIXME + // 加一层containerGroup是为了clip,但是现在clip功能并没有实现。 + containerGroup = this._containerGroup = new Group(); + this._initEvents(containerGroup); + this.group.add(containerGroup); + } + containerGroup.attr('position', [layoutInfo.x, layoutInfo.y]); + + return containerGroup; + }, + + /** + * @private + */ + _doRender: function (containerGroup, seriesModel, reRoot) { + var thisTree = seriesModel.getData().tree; + var oldTree = this._oldTree; + + // Clear last shape records. + var lastsForAnimation = createStorage(); + var thisStorage = createStorage(); + var oldStorage = this._storage; + var willInvisibleEls = []; + var doRenderNode = zrUtil.curry( + renderNode, seriesModel, + thisStorage, oldStorage, reRoot, + lastsForAnimation, willInvisibleEls + ); + + // Notice: when thisTree and oldTree are the same tree (see list.cloneShadow), + // the oldTree is actually losted, so we can not find all of the old graphic + // elements from tree. So we use this stragegy: make element storage, move + // from old storage to new storage, clear old storage. + + dualTravel( + thisTree.root ? [thisTree.root] : [], + (oldTree && oldTree.root) ? [oldTree.root] : [], + containerGroup, + thisTree === oldTree || !oldTree, + 0 + ); + + // Process all removing. + var willDeleteEls = clearStorage(oldStorage); + + this._oldTree = thisTree; + this._storage = thisStorage; + + return { + lastsForAnimation: lastsForAnimation, + willDeleteEls: willDeleteEls, + renderFinally: renderFinally + }; + + function dualTravel(thisViewChildren, oldViewChildren, parentGroup, sameTree, depth) { + // When 'render' is triggered by action, + // 'this' and 'old' may be the same tree, + // we use rawIndex in that case. + if (sameTree) { + oldViewChildren = thisViewChildren; + each(thisViewChildren, function (child, index) { + !child.isRemoved() && processNode(index, index); + }); + } + // Diff hierarchically (diff only in each subtree, but not whole). + // because, consistency of view is important. + else { + (new DataDiffer(oldViewChildren, thisViewChildren, getKey, getKey)) + .add(processNode) + .update(processNode) + .remove(zrUtil.curry(processNode, null)) + .execute(); + } + + function getKey(node) { + // Identify by name or raw index. + return node.getId(); + } + + function processNode(newIndex, oldIndex) { + var thisNode = newIndex != null ? thisViewChildren[newIndex] : null; + var oldNode = oldIndex != null ? oldViewChildren[oldIndex] : null; + + var group = doRenderNode(thisNode, oldNode, parentGroup, depth); + + group && dualTravel( + thisNode && thisNode.viewChildren || [], + oldNode && oldNode.viewChildren || [], + group, + sameTree, + depth + 1 + ); + } + } + + function clearStorage(storage) { + var willDeleteEls = createStorage(); + storage && each(storage, function (store, storageName) { + var delEls = willDeleteEls[storageName]; + each(store, function (el) { + el && (delEls.push(el), el.__tmWillDelete = 1); + }); + }); + return willDeleteEls; + } + + function renderFinally() { + each(willDeleteEls, function (els) { + each(els, function (el) { + el.parent && el.parent.remove(el); + }); + }); + each(willInvisibleEls, function (el) { + el.invisible = true; + // Setting invisible is for optimizing, so no need to set dirty, + // just mark as invisible. + el.dirty(); + }); + } + }, + + /** + * @private + */ + _doAnimation: function (containerGroup, renderResult, seriesModel, reRoot) { + if (!seriesModel.get('animation')) { + return; + } + + var duration = seriesModel.get('animationDurationUpdate'); + var easing = seriesModel.get('animationEasing'); + var animationWrap = animationUtil.createWrap(); + + // Make delete animations. + each(renderResult.willDeleteEls, function (store, storageName) { + each(store, function (el, rawIndex) { + if (el.invisible) { + return; + } + + var parent = el.parent; // Always has parent, and parent is nodeGroup. + var target; + + if (reRoot && reRoot.direction === 'drillDown') { + target = parent === reRoot.rootNodeGroup + // This is the content element of view root. + // Only `content` will enter this branch, because + // `background` and `nodeGroup` will not be deleted. + ? { + shape: { + x: 0, + y: 0, + width: parent.__tmNodeWidth, + height: parent.__tmNodeHeight + }, + style: { + opacity: 0 + } + } + // Others. + : {style: {opacity: 0}}; + } + else { + var targetX = 0; + var targetY = 0; + + if (!parent.__tmWillDelete) { + // Let node animate to right-bottom corner, cooperating with fadeout, + // which is appropriate for user understanding. + // Divided by 2 for reRoot rolling up effect. + targetX = parent.__tmNodeWidth / 2; + targetY = parent.__tmNodeHeight / 2; + } + + target = storageName === 'nodeGroup' + ? {position: [targetX, targetY], style: {opacity: 0}} + : { + shape: {x: targetX, y: targetY, width: 0, height: 0}, + style: {opacity: 0} + }; + } + + target && animationWrap.add(el, target, duration, easing); + }); + }); + + // Make other animations + each(this._storage, function (store, storageName) { + each(store, function (el, rawIndex) { + var last = renderResult.lastsForAnimation[storageName][rawIndex]; + var target = {}; + + if (!last) { + return; + } + + if (storageName === 'nodeGroup') { + if (last.old) { + target.position = el.position.slice(); + el.attr('position', last.old); + } + } + else { + if (last.old) { + target.shape = zrUtil.extend({}, el.shape); + el.setShape(last.old); + } + + if (last.fadein) { + el.setStyle('opacity', 0); + target.style = {opacity: 1}; + } + // When animation is stopped for succedent animation starting, + // el.style.opacity might not be 1 + else if (el.style.opacity !== 1) { + target.style = {opacity: 1}; + } + } + + animationWrap.add(el, target, duration, easing); + }); + }, this); + + this._state = 'animating'; + + animationWrap + .done(bind(function () { + this._state = 'ready'; + renderResult.renderFinally(); + }, this)) + .start(); + }, + + /** + * @private + */ + _resetController: function (api) { + var controller = this._controller; + + // Init controller. + if (!controller) { + controller = this._controller = new RoamController(api.getZr()); + controller.enable(this.seriesModel.get('roam')); + controller.on('pan', bind(this._onPan, this)); + controller.on('zoom', bind(this._onZoom, this)); + } + + var rect = new BoundingRect(0, 0, api.getWidth(), api.getHeight()); + controller.rectProvider = function () { + return rect; + }; + }, + + /** + * @private + */ + _clearController: function () { + var controller = this._controller; + if (controller) { + controller.off('pan').off('zoom'); + controller = null; + } + }, + + /** + * @private + */ + _onPan: function (dx, dy) { + this._mayClick = false; + + if (this._state !== 'animating' + && (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) + ) { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + + if (!root) { + return; + } + + var rootLayout = root.getLayout(); + + if (!rootLayout) { + return; + } + + this.api.dispatchAction({ + type: 'treemapMove', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rootLayout.x + dx, y: rootLayout.y + dy, + width: rootLayout.width, height: rootLayout.height + } + }); + } + }, + + /** + * @private + */ + _onZoom: function (scale, mouseX, mouseY) { + this._mayClick = false; + + if (this._state !== 'animating') { + // These param must not be cached. + var root = this.seriesModel.getData().tree.root; + + if (!root) { + return; + } + + var rootLayout = root.getLayout(); + + if (!rootLayout) { + return; + } + + var rect = new BoundingRect( + rootLayout.x, rootLayout.y, rootLayout.width, rootLayout.height + ); + var layoutInfo = this.seriesModel.layoutInfo; + + // Transform mouse coord from global to containerGroup. + mouseX -= layoutInfo.x; + mouseY -= layoutInfo.y; + + // Scale root bounding rect. + var m = matrix.create(); + matrix.translate(m, m, [-mouseX, -mouseY]); + matrix.scale(m, m, [scale, scale]); + matrix.translate(m, m, [mouseX, mouseY]); + + rect.applyTransform(m); + + this.api.dispatchAction({ + type: 'treemapRender', + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: { + x: rect.x, y: rect.y, + width: rect.width, height: rect.height + } + }); + } + }, + + /** + * @private + */ + _initEvents: function (containerGroup) { + // FIXME + // 不用click以及silent的原因是,animate时视图设置silent true来避免click生效, + // 但是animate中,按下鼠标,animate结束后(silent设回为false)松开鼠标, + // 还是会触发click,期望是不触发。 + + // Mousedown occurs when drag start, and mouseup occurs when drag end, + // click event should not be triggered in that case. + + containerGroup.on('mousedown', function (e) { + this._state === 'ready' && (this._mayClick = true); + }, this); + containerGroup.on('mouseup', function (e) { + if (this._mayClick) { + this._mayClick = false; + this._state === 'ready' && onClick.call(this, e); + } + }, this); + + function onClick(e) { + var nodeClick = this.seriesModel.get('nodeClick', true); + + if (!nodeClick) { + return; + } + + var targetInfo = this.findTarget(e.offsetX, e.offsetY); + + if (!targetInfo) { + return; + } + + var node = targetInfo.node; + if (node.getLayout().isLeafRoot) { + this._rootToNode(targetInfo); + } + else { + if (nodeClick === 'zoomToNode') { + this._zoomToNode(targetInfo); + } + else if (nodeClick === 'link') { + var itemModel = node.hostTree.data.getItemModel(node.dataIndex); + var link = itemModel.get('link', true); + var linkTarget = itemModel.get('target', true) || 'blank'; + link && window.open(link, linkTarget); + } + } + } + }, + + /** + * @private + */ + _renderBreadcrumb: function (seriesModel, api, targetInfo) { + if (!targetInfo) { + // Find breadcrumb tail on center of containerGroup. + targetInfo = this.findTarget(api.getWidth() / 2, api.getHeight() / 2); + + if (!targetInfo) { + targetInfo = {node: seriesModel.getData().tree.root}; + } + } + + (this._breadcrumb || (this._breadcrumb = new Breadcrumb(this.group, bind(onSelect, this)))) + .render(seriesModel, api, targetInfo.node); + + function onSelect(node) { + if (this._state !== 'animating') { + helper.aboveViewRoot(seriesModel.getViewRoot(), node) + ? this._rootToNode({node: node}) + : this._zoomToNode({node: node}); + } + } + }, + + /** + * @override + */ + remove: function () { + this._clearController(); + this._containerGroup && this._containerGroup.removeAll(); + this._storage = createStorage(); + this._state = 'ready'; + this._breadcrumb && this._breadcrumb.remove(); + }, + + dispose: function () { + this._clearController(); + }, + + /** + * @private + */ + _zoomToNode: function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapZoomToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }, + + /** + * @private + */ + _rootToNode: function (targetInfo) { + this.api.dispatchAction({ + type: 'treemapRootToNode', + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: targetInfo.node + }); + }, + + /** + * @public + * @param {number} x Global coord x. + * @param {number} y Global coord y. + * @return {Object} info If not found, return undefined; + * @return {number} info.node Target node. + * @return {number} info.offsetX x refer to target node. + * @return {number} info.offsetY y refer to target node. + */ + findTarget: function (x, y) { + var targetInfo; + var viewRoot = this.seriesModel.getViewRoot(); + + viewRoot.eachNode({attr: 'viewChildren', order: 'preorder'}, function (node) { + var bgEl = this._storage.background[node.getRawIndex()]; + // If invisible, there might be no element. + if (bgEl) { + var point = bgEl.transformCoordToLocal(x, y); + var shape = bgEl.shape; + + // For performance consideration, dont use 'getBoundingRect'. + if (shape.x <= point[0] + && point[0] <= shape.x + shape.width + && shape.y <= point[1] + && point[1] <= shape.y + shape.height + ) { + targetInfo = {node: node, offsetX: point[0], offsetY: point[1]}; + } + else { + return false; // Suppress visit subtree. + } + } + }, this); + + return targetInfo; + } + + }); + + /** + * @inner + */ + function createStorage() { + return {nodeGroup: [], background: [], content: []}; + } + + /** + * @inner + * @return Return undefined means do not travel further. + */ + function renderNode( + seriesModel, thisStorage, oldStorage, reRoot, + lastsForAnimation, willInvisibleEls, + thisNode, oldNode, parentGroup, depth + ) { + // Whether under viewRoot. + if (!thisNode) { + // Deleting nodes will be performed finally. This method just find + // element from old storage, or create new element, set them to new + // storage, and set styles. + return; + } + + var thisLayout = thisNode.getLayout(); + + if (!thisLayout || !thisLayout.isInView) { + return; + } + + var thisWidth = thisLayout.width; + var thisHeight = thisLayout.height; + var thisInvisible = thisLayout.invisible; + + var thisRawIndex = thisNode.getRawIndex(); + var oldRawIndex = oldNode && oldNode.getRawIndex(); + + // Node group + var group = giveGraphic('nodeGroup', Group); + + if (!group) { + return; + } + + parentGroup.add(group); + // x,y are not set when el is above view root. + group.attr('position', [thisLayout.x || 0, thisLayout.y || 0]); + group.__tmNodeWidth = thisWidth; + group.__tmNodeHeight = thisHeight; + + if (thisLayout.isAboveViewRoot) { + return group; + } + + // Background + var bg = giveGraphic('background', Rect, depth, Z_BG); + if (bg) { + bg.setShape({x: 0, y: 0, width: thisWidth, height: thisHeight}); + updateStyle(bg, function () { + bg.setStyle('fill', thisNode.getVisual('borderColor', true)); + }); + group.add(bg); + } + + var thisViewChildren = thisNode.viewChildren; + + // No children, render content. + if (!thisViewChildren || !thisViewChildren.length) { + var content = giveGraphic('content', Rect, depth, Z_CONTENT); + content && renderContent(group); + } + + return group; + + // ---------------------------- + // | Procedures in renderNode | + // ---------------------------- + + function renderContent(group) { + // For tooltip. + content.dataIndex = thisNode.dataIndex; + content.seriesIndex = seriesModel.seriesIndex; + + var borderWidth = thisLayout.borderWidth; + var contentWidth = Math.max(thisWidth - 2 * borderWidth, 0); + var contentHeight = Math.max(thisHeight - 2 * borderWidth, 0); + + content.culling = true; + content.setShape({ + x: borderWidth, + y: borderWidth, + width: contentWidth, + height: contentHeight + }); + + var visualColor = thisNode.getVisual('color', true); + updateStyle(content, function () { + var normalStyle = {fill: visualColor}; + var emphasisStyle = thisNode.getModel('itemStyle.emphasis').getItemStyle(); + + prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight); + + content.setStyle(normalStyle); + graphic.setHoverStyle(content, emphasisStyle); + }); + + group.add(content); + } + + function updateStyle(element, cb) { + if (!thisInvisible) { + // If invisible, do not set visual, otherwise the element will + // change immediately before animation. We think it is OK to + // remain its origin color when moving out of the view window. + cb(); + + if (!element.__tmWillVisible) { + element.invisible = false; + } + } + else { + // Delay invisible setting utill animation finished, + // avoid element vanish suddenly before animation. + !element.invisible && willInvisibleEls.push(element); + } + } + + function prepareText(normalStyle, emphasisStyle, visualColor, contentWidth, contentHeight) { + var nodeModel = thisNode.getModel(); + var text = nodeModel.get('name'); + if (thisLayout.isLeafRoot) { + var iconChar = seriesModel.get('drillDownIcon', true); + text = iconChar ? iconChar + ' ' + text : test; + } + + setText( + text, normalStyle, nodeModel, PATH_LABEL_NORMAL, + visualColor, contentWidth, contentHeight + ); + setText( + text, emphasisStyle, nodeModel, PATH_LABEL_EMPHASIS, + visualColor, contentWidth, contentHeight + ); + } + + function setText(text, style, nodeModel, labelPath, visualColor, contentWidth, contentHeight) { + var labelModel = nodeModel.getModel(labelPath); + var labelTextStyleModel = labelModel.getModel('textStyle'); + + graphic.setText(style, labelModel, visualColor); + + // text.align and text.baseline is not included by graphic.setText, + // because in most cases the two attributes are not exposed to user, + // except in treemap. + style.textAlign = labelTextStyleModel.get('align'); + style.textVerticalAlign = labelTextStyleModel.get('baseline'); + + var textRect = labelTextStyleModel.getTextRect(text); + if (!labelModel.getShallow('show') || textRect.height > contentHeight) { + style.text = ''; + } + else if (textRect.width > contentWidth) { + style.text = labelTextStyleModel.get('ellipsis') + ? labelTextStyleModel.truncateText( + text, contentWidth, null, {minChar: 2} + ) + : ''; + } + else { + style.text = text; + } + } + + function giveGraphic(storageName, Ctor, depth, z) { + var element = oldRawIndex != null && oldStorage[storageName][oldRawIndex]; + var lasts = lastsForAnimation[storageName]; + + if (element) { + // Remove from oldStorage + oldStorage[storageName][oldRawIndex] = null; + prepareAnimationWhenHasOld(lasts, element, storageName); + } + // If invisible and no old element, do not create new element (for optimizing). + else if (!thisInvisible) { + element = new Ctor({z: calculateZ(depth, z)}); + element.__tmDepth = depth; + element.__tmStorageName = storageName; + prepareAnimationWhenNoOld(lasts, element, storageName); + } + + // Set to thisStorage + return (thisStorage[storageName][thisRawIndex] = element); + } + + function prepareAnimationWhenHasOld(lasts, element, storageName) { + var lastCfg = lasts[thisRawIndex] = {}; + lastCfg.old = storageName === 'nodeGroup' + ? element.position.slice() + : zrUtil.extend({}, element.shape); + } + + // If a element is new, we need to find the animation start point carefully, + // otherwise it will looks strange when 'zoomToNode'. + function prepareAnimationWhenNoOld(lasts, element, storageName) { + var lastCfg = lasts[thisRawIndex] = {}; + var parentNode = thisNode.parentNode; + + if (parentNode && (!reRoot || reRoot.direction === 'drillDown')) { + var parentOldX = 0; + var parentOldY = 0; + + // New nodes appear from right-bottom corner in 'zoomToNode' animation. + // For convenience, get old bounding rect from background. + var parentOldBg = lastsForAnimation.background[parentNode.getRawIndex()]; + if (!reRoot && parentOldBg && parentOldBg.old) { + parentOldX = parentOldBg.old.width; + parentOldY = parentOldBg.old.height; + } + + // When no parent old shape found, its parent is new too, + // so we can just use {x:0, y:0}. + lastCfg.old = storageName === 'nodeGroup' + ? [0, parentOldY] + : {x: parentOldX, y: parentOldY, width: 0, height: 0}; + } + + // Fade in, user can be aware that these nodes are new. + lastCfg.fadein = storageName !== 'nodeGroup'; + } + } + + // We can not set all backgroud with the same z, Because the behaviour of + // drill down and roll up differ background creation sequence from tree + // hierarchy sequence, which cause that lowser background element overlap + // upper ones. So we calculate z based on depth. + // Moreover, we try to shrink down z interval to [0, 1] to avoid that + // treemap with large z overlaps other components. + function calculateZ(depth, zInLevel) { + var zb = depth * Z_BASE + zInLevel; + return (zb - 1) / zb; + } + + + +/***/ }, +/* 187 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + var helper = { + + retrieveTargetInfo: function (payload, seriesModel) { + if (payload + && ( + payload.type === 'treemapZoomToNode' + || payload.type === 'treemapRootToNode' + ) + ) { + var root = seriesModel.getData().tree.root; + var targetNode = payload.targetNode; + if (targetNode && root.contains(targetNode)) { + return {node: targetNode}; + } + + var targetNodeId = payload.targetNodeId; + if (targetNodeId != null && (targetNode = root.getNodeById(targetNodeId))) { + return {node: targetNode}; + } + } + }, + + // Not includes the given node at the last item. + getPathToRoot: function (node) { + var path = []; + while (node) { + node = node.parentNode; + node && path.push(node); + } + return path.reverse(); + }, + + aboveViewRoot: function (viewRoot, node) { + var viewPath = helper.getPathToRoot(viewRoot); + return zrUtil.indexOf(viewPath, node) >= 0; + } + }; + + module.exports = helper; + + +/***/ }, +/* 188 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + var zrUtil = __webpack_require__(4); + + var TEXT_PADDING = 8; + var ITEM_GAP = 8; + var ARRAY_LENGTH = 5; + + function Breadcrumb(containerGroup, onSelect) { + /** + * @private + * @type {module:zrender/container/Group} + */ + this.group = new graphic.Group(); + + containerGroup.add(this.group); + + /** + * @private + * @type {Function} + */ + this._onSelect = onSelect || zrUtil.noop; + } + + Breadcrumb.prototype = { + + constructor: Breadcrumb, + + render: function (seriesModel, api, targetNode) { + var model = seriesModel.getModel('breadcrumb'); + var thisGroup = this.group; + + thisGroup.removeAll(); + + if (!model.get('show') || !targetNode) { + return; + } + + var normalStyleModel = model.getModel('itemStyle.normal'); + // var emphasisStyleModel = model.getModel('itemStyle.emphasis'); + var textStyleModel = normalStyleModel.getModel('textStyle'); + + var layoutParam = { + pos: { + left: model.get('left'), + right: model.get('right'), + top: model.get('top'), + bottom: model.get('bottom') + }, + box: { + width: api.getWidth(), + height: api.getHeight() + }, + emptyItemWidth: model.get('emptyItemWidth'), + totalWidth: 0, + renderList: [] + }; + + this._prepare( + model, targetNode, layoutParam, textStyleModel + ); + this._renderContent( + model, targetNode, layoutParam, normalStyleModel, textStyleModel + ); + + layout.positionGroup(thisGroup, layoutParam.pos, layoutParam.box); + }, + + /** + * Prepare render list and total width + * @private + */ + _prepare: function (model, targetNode, layoutParam, textStyleModel) { + for (var node = targetNode; node; node = node.parentNode) { + var text = node.getModel().get('name'); + var textRect = textStyleModel.getTextRect(text); + var itemWidth = Math.max( + textRect.width + TEXT_PADDING * 2, + layoutParam.emptyItemWidth + ); + layoutParam.totalWidth += itemWidth + ITEM_GAP; + layoutParam.renderList.push({node: node, text: text, width: itemWidth}); + } + }, + + /** + * @private + */ + _renderContent: function ( + model, targetNode, layoutParam, normalStyleModel, textStyleModel + ) { + // Start rendering. + var lastX = 0; + var emptyItemWidth = layoutParam.emptyItemWidth; + var height = model.get('height'); + var availableSize = layout.getAvailableSize(layoutParam.pos, layoutParam.box); + var totalWidth = layoutParam.totalWidth; + var renderList = layoutParam.renderList; + + for (var i = renderList.length - 1; i >= 0; i--) { + var item = renderList[i]; + var itemWidth = item.width; + var text = item.text; + + // Hdie text and shorten width if necessary. + if (totalWidth > availableSize.width) { + totalWidth -= itemWidth - emptyItemWidth; + itemWidth = emptyItemWidth; + text = ''; + } + + this.group.add(new graphic.Polygon({ + shape: { + points: makeItemPoints( + lastX, 0, itemWidth, height, + i === renderList.length - 1, i === 0 + ) + }, + style: zrUtil.defaults( + normalStyleModel.getItemStyle(), + { + lineJoin: 'bevel', + text: text, + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + } + ), + z: 10, + onclick: zrUtil.bind(this._onSelect, this, item.node) + })); + + lastX += itemWidth + ITEM_GAP; + } + }, + + /** + * @override + */ + remove: function () { + this.group.removeAll(); + } + }; + + function makeItemPoints(x, y, itemWidth, itemHeight, head, tail) { + var points = [ + [head ? x : x - ARRAY_LENGTH, y], + [x + itemWidth, y], + [x + itemWidth, y + itemHeight], + [head ? x : x - ARRAY_LENGTH, y + itemHeight] + ]; + !tail && points.splice(2, 0, [x + itemWidth + ARRAY_LENGTH, y + itemHeight / 2]); + !head && points.push([x, y + itemHeight / 2]); + return points; + } + + module.exports = Breadcrumb; + + +/***/ }, +/* 189 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + /** + * @param {number} [time=500] Time in ms + * @param {string} [easing='linear'] + * @param {number} [delay=0] + * @param {Function} [callback] + * + * @example + * // Animate position + * animation + * .createWrap() + * .add(el1, {position: [10, 10]}) + * .add(el2, {shape: {width: 500}, style: {fill: 'red'}}, 400) + * .done(function () { // done }) + * .start('cubicOut'); + */ + function createWrap() { + + var storage = []; + var elExistsMap = {}; + var doneCallback; + + return { + + /** + * Caution: a el can only be added once, otherwise 'done' + * might not be called. This method checks this (by el.id), + * suppresses adding and returns false when existing el found. + * + * @param {modele:zrender/Element} el + * @param {Object} target + * @param {number} [time=500] + * @param {number} [delay=0] + * @param {string} [easing='linear'] + * @return {boolean} Whether adding succeeded. + * + * @example + * add(el, target, time, delay, easing); + * add(el, target, time, easing); + * add(el, target, time); + * add(el, target); + */ + add: function (el, target, time, delay, easing) { + if (zrUtil.isString(delay)) { + easing = delay; + delay = 0; + } + + if (elExistsMap[el.id]) { + return false; + } + elExistsMap[el.id] = 1; + + storage.push( + {el: el, target: target, time: time, delay: delay, easing: easing} + ); + + return true; + }, + + /** + * Only execute when animation finished. Will not execute when any + * of 'stop' or 'stopAnimation' called. + * + * @param {Function} callback + */ + done: function (callback) { + doneCallback = callback; + return this; + }, + + /** + * Will stop exist animation firstly. + */ + start: function () { + var count = storage.length; + + for (var i = 0, len = storage.length; i < len; i++) { + var item = storage[i]; + item.el.animateTo(item.target, item.time, item.delay, item.easing, done); + } + + return this; + + function done() { + count--; + if (!count) { + storage.length = 0; + elExistsMap = {}; + doneCallback && doneCallback(); + } + } + } + }; + } + + module.exports = {createWrap: createWrap}; + + +/***/ }, +/* 190 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Treemap action + */ + + + var echarts = __webpack_require__(1); + var helper = __webpack_require__(187); + + var noop = function () {}; + + var actionTypes = [ + 'treemapZoomToNode', + 'treemapRender', + 'treemapMove' + ]; + + for (var i = 0; i < actionTypes.length; i++) { + echarts.registerAction({type: actionTypes[i], update: 'updateView'}, noop); + } + + echarts.registerAction( + {type: 'treemapRootToNode', update: 'updateView'}, + function (payload, ecModel) { + + ecModel.eachComponent( + {mainType: 'series', subType: 'treemap', query: payload}, + handleRootToNode + ); + + function handleRootToNode(model, index) { + var targetInfo = helper.retrieveTargetInfo(payload, model); + + if (targetInfo) { + var originViewRoot = model.getViewRoot(); + if (originViewRoot) { + payload.direction = helper.aboveViewRoot(originViewRoot, targetInfo.node) + ? 'rollUp' : 'drillDown'; + } + model.resetViewRoot(targetInfo.node); + } + } + } + ); + + + +/***/ }, +/* 191 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapping = __webpack_require__(192); + var zrColor = __webpack_require__(39); + var zrUtil = __webpack_require__(4); + var isArray = zrUtil.isArray; + + var ITEM_STYLE_NORMAL = 'itemStyle.normal'; + + module.exports = function (ecModel, api, payload) { + + var condition = {mainType: 'series', subType: 'treemap', query: payload}; + ecModel.eachComponent(condition, function (seriesModel) { + + var tree = seriesModel.getData().tree; + var root = tree.root; + var seriesItemStyleModel = seriesModel.getModel(ITEM_STYLE_NORMAL); + + if (root.isRemoved()) { + return; + } + + var levelItemStyles = zrUtil.map(tree.levelModels, function (levelModel) { + return levelModel ? levelModel.get(ITEM_STYLE_NORMAL) : null; + }); + + travelTree( + root, // Visual should calculate from tree root but not view root. + {}, + levelItemStyles, + seriesItemStyleModel, + seriesModel.getViewRoot().getAncestors(), + seriesModel + ); + }); + }; + + function travelTree( + node, designatedVisual, levelItemStyles, seriesItemStyleModel, + viewRootAncestors, seriesModel + ) { + var nodeModel = node.getModel(); + var nodeLayout = node.getLayout(); + + // Optimize + if (!nodeLayout || nodeLayout.invisible || !nodeLayout.isInView) { + return; + } + + var nodeItemStyleModel = node.getModel(ITEM_STYLE_NORMAL); + var levelItemStyle = levelItemStyles[node.depth]; + var visuals = buildVisuals( + nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel + ); + + // calculate border color + var borderColor = nodeItemStyleModel.get('borderColor'); + var borderColorSaturation = nodeItemStyleModel.get('borderColorSaturation'); + var thisNodeColor; + if (borderColorSaturation != null) { + // For performance, do not always execute 'calculateColor'. + thisNodeColor = calculateColor(visuals, node); + borderColor = calculateBorderColor(borderColorSaturation, thisNodeColor); + } + node.setVisual('borderColor', borderColor); + + var viewChildren = node.viewChildren; + if (!viewChildren || !viewChildren.length) { + thisNodeColor = calculateColor(visuals, node); + // Apply visual to this node. + node.setVisual('color', thisNodeColor); + } + else { + var mapping = buildVisualMapping( + node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren + ); + // Designate visual to children. + zrUtil.each(viewChildren, function (child, index) { + // If higher than viewRoot, only ancestors of viewRoot is needed to visit. + if (child.depth >= viewRootAncestors.length + || child === viewRootAncestors[child.depth] + ) { + var childVisual = mapVisual( + nodeModel, visuals, child, index, mapping, seriesModel + ); + travelTree( + child, childVisual, levelItemStyles, seriesItemStyleModel, + viewRootAncestors, seriesModel + ); + } + }); + } + } + + function buildVisuals( + nodeItemStyleModel, designatedVisual, levelItemStyle, seriesItemStyleModel + ) { + var visuals = zrUtil.extend({}, designatedVisual); + + zrUtil.each(['color', 'colorAlpha', 'colorSaturation'], function (visualName) { + // Priority: thisNode > thisLevel > parentNodeDesignated > seriesModel + var val = nodeItemStyleModel.get(visualName, true); // Ignore parent + val == null && levelItemStyle && (val = levelItemStyle[visualName]); + val == null && (val = designatedVisual[visualName]); + val == null && (val = seriesItemStyleModel.get(visualName)); + + val != null && (visuals[visualName] = val); + }); + + return visuals; + } + + function calculateColor(visuals) { + var color = getValueVisualDefine(visuals, 'color'); + + if (color) { + var colorAlpha = getValueVisualDefine(visuals, 'colorAlpha'); + var colorSaturation = getValueVisualDefine(visuals, 'colorSaturation'); + if (colorSaturation) { + color = zrColor.modifyHSL(color, null, null, colorSaturation); + } + if (colorAlpha) { + color = zrColor.modifyAlpha(color, colorAlpha); + } + + return color; + } + } + + function calculateBorderColor(borderColorSaturation, thisNodeColor) { + return thisNodeColor != null + ? zrColor.modifyHSL(thisNodeColor, null, null, borderColorSaturation) + : null; + } + + function getValueVisualDefine(visuals, name) { + var value = visuals[name]; + if (value != null && value !== 'none') { + return value; + } + } + + function buildVisualMapping( + node, nodeModel, nodeLayout, nodeItemStyleModel, visuals, viewChildren + ) { + if (!viewChildren || !viewChildren.length) { + return; + } + + var rangeVisual = getRangeVisual(nodeModel, 'color') + || ( + visuals.color != null + && visuals.color !== 'none' + && ( + getRangeVisual(nodeModel, 'colorAlpha') + || getRangeVisual(nodeModel, 'colorSaturation') + ) + ); + + if (!rangeVisual) { + return; + } + + var colorMappingBy = nodeModel.get('colorMappingBy'); + var opt = { + type: rangeVisual.name, + dataExtent: nodeLayout.dataExtent, + visual: rangeVisual.range + }; + if (opt.type === 'color' + && (colorMappingBy === 'index' || colorMappingBy === 'id') + ) { + opt.mappingMethod = 'category'; + opt.loop = true; + // categories is ordinal, so do not set opt.categories. + } + else { + opt.mappingMethod = 'linear'; + } + + var mapping = new VisualMapping(opt); + mapping.__drColorMappingBy = colorMappingBy; + + return mapping; + } + + // Notice: If we dont have the attribute 'colorRange', but only use + // attribute 'color' to represent both concepts of 'colorRange' and 'color', + // (It means 'colorRange' when 'color' is Array, means 'color' when not array), + // this problem will be encountered: + // If a level-1 node dont have children, and its siblings has children, + // and colorRange is set on level-1, then the node can not be colored. + // So we separate 'colorRange' and 'color' to different attributes. + function getRangeVisual(nodeModel, name) { + // 'colorRange', 'colorARange', 'colorSRange'. + // If not exsits on this node, fetch from levels and series. + var range = nodeModel.get(name); + return (isArray(range) && range.length) ? {name: name, range: range} : null; + } + + function mapVisual(nodeModel, visuals, child, index, mapping, seriesModel) { + var childVisuals = zrUtil.extend({}, visuals); + + if (mapping) { + var mappingType = mapping.type; + var colorMappingBy = mappingType === 'color' && mapping.__drColorMappingBy; + var value = + colorMappingBy === 'index' + ? index + : colorMappingBy === 'id' + ? seriesModel.mapIdToIndex(child.getId()) + : child.getValue(nodeModel.get('visualDimension')); + + childVisuals[mappingType] = mapping.mapValueToVisual(value); + } + + return childVisuals; + } + + + +/***/ }, +/* 192 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual mapping. + */ + + + var zrUtil = __webpack_require__(4); + var zrColor = __webpack_require__(39); + var linearMap = __webpack_require__(7).linearMap; + var each = zrUtil.each; + var isObject = zrUtil.isObject; + + var CATEGORY_DEFAULT_VISUAL_INDEX = -1; + + /** + * @param {Object} option + * @param {string} [option.type] See visualHandlers. + * @param {string} [option.mappingMethod] 'linear' or 'piecewise' or 'category' or 'fixed' + * @param {Array.=} [option.dataExtent] [minExtent, maxExtent], + * required when mappingMethod is 'linear' + * @param {Array.=} [option.pieceList] [ + * {value: someValue}, + * {interval: [min1, max1], visual: {...}}, + * {interval: [min2, max2]} + * ], + * required when mappingMethod is 'piecewise'. + * Visual for only each piece can be specified. + * @param {Array.=} [option.categories] ['cate1', 'cate2'] + * required when mappingMethod is 'category'. + * If no option.categories, categories is set + * as [0, 1, 2, ...]. + * @param {boolean} [option.loop=false] Whether loop mapping when mappingMethod is 'category'. + * @param {(Array|Object|*)} [option.visual] Visual data. + * when mappingMethod is 'category', + * visual data can be array or object + * (like: {cate1: '#222', none: '#fff'}) + * or primary types (which represents + * defualt category visual), otherwise visual + * can be array or primary (which will be + * normalized to array). + * + */ + var VisualMapping = function (option) { + var mappingMethod = option.mappingMethod; + var visualType = option.type; + + /** + * @readOnly + * @type {Object} + */ + var thisOption = this.option = zrUtil.clone(option); + + /** + * @readOnly + * @type {string} + */ + this.type = visualType; + + /** + * @readOnly + * @type {string} + */ + this.mappingMethod = mappingMethod; + + /** + * @private + * @type {Function} + */ + this._normalizeData = normalizers[mappingMethod]; + + var visualHandler = visualHandlers[visualType]; + + /** + * @public + * @type {Function} + */ + this.applyVisual = visualHandler.applyVisual; + + /** + * @public + * @type {Function} + */ + this.getColorMapper = visualHandler.getColorMapper; + + /** + * @private + * @type {Function} + */ + this._doMap = visualHandler._doMap[mappingMethod]; + + if (mappingMethod === 'piecewise') { + normalizeVisualRange(thisOption); + preprocessForPiecewise(thisOption); + } + else if (mappingMethod === 'category') { + thisOption.categories + ? preprocessForSpecifiedCategory(thisOption) + // categories is ordinal when thisOption.categories not specified, + // which need no more preprocess except normalize visual. + : normalizeVisualRange(thisOption, true); + } + else { // mappingMethod === 'linear' or 'fixed' + zrUtil.assert(mappingMethod !== 'linear' || thisOption.dataExtent); + normalizeVisualRange(thisOption); + } + }; + + VisualMapping.prototype = { + + constructor: VisualMapping, + + mapValueToVisual: function (value) { + var normalized = this._normalizeData(value); + return this._doMap(normalized, value); + }, + + getNormalizer: function () { + return zrUtil.bind(this._normalizeData, this); + } + }; + + var visualHandlers = VisualMapping.visualHandlers = { + + color: { + + applyVisual: makeApplyVisual('color'), + + /** + * Create a mapper function + * @return {Function} + */ + getColorMapper: function () { + var thisOption = this.option; + var parsedVisual = zrUtil.map(thisOption.visual, zrColor.parse); + + return zrUtil.bind( + thisOption.mappingMethod === 'category' + ? function (value, isNormalized) { + !isNormalized && (value = this._normalizeData(value)); + return doMapCategory(this, value); + } + : function (value, isNormalized, out) { + // If output rgb array + // which will be much faster and useful in pixel manipulation + var returnRGBArray = !!out; + !isNormalized && (value = this._normalizeData(value)); + out = zrColor.fastMapToColor(value, parsedVisual, out); + return returnRGBArray ? out : zrUtil.stringify(out, 'rgba'); + }, + this + ); + }, + + _doMap: { + linear: function (normalized) { + return zrColor.mapToColor(normalized, this.option.visual); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = zrColor.mapToColor(normalized, this.option.visual); + } + return result; + }, + fixed: doMapFixed + } + }, + + colorHue: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, value); + }), + + colorSaturation: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, null, value); + }), + + colorLightness: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyHSL(color, null, null, value); + }), + + colorAlpha: makePartialColorVisualHandler(function (color, value) { + return zrColor.modifyAlpha(color, value); + }), + + opacity: { + applyVisual: makeApplyVisual('opacity'), + _doMap: makeDoMap([0, 1]) + }, + + symbol: { + applyVisual: function (value, getter, setter) { + var symbolCfg = this.mapValueToVisual(value); + if (zrUtil.isString(symbolCfg)) { + setter('symbol', symbolCfg); + } + else if (isObject(symbolCfg)) { + for (var name in symbolCfg) { + if (symbolCfg.hasOwnProperty(name)) { + setter(name, symbolCfg[name]); + } + } + } + }, + _doMap: { + linear: doMapToArray, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = doMapToArray.call(this, normalized); + } + return result; + }, + fixed: doMapFixed + } + }, + + symbolSize: { + applyVisual: makeApplyVisual('symbolSize'), + _doMap: makeDoMap([0, 1]) + } + }; + + + function preprocessForPiecewise(thisOption) { + var pieceList = thisOption.pieceList; + thisOption.hasSpecialVisual = false; + + zrUtil.each(pieceList, function (piece, index) { + piece.originIndex = index; + // piece.visual is "result visual value" but not + // a visual range, so it does not need to be normalized. + if (piece.visual != null) { + thisOption.hasSpecialVisual = true; + } + }); + } + + function preprocessForSpecifiedCategory(thisOption) { + // Hash categories. + var categories = thisOption.categories; + var visual = thisOption.visual; + + var categoryMap = thisOption.categoryMap = {}; + each(categories, function (cate, index) { + categoryMap[cate] = index; + }); + + // Process visual map input. + if (!zrUtil.isArray(visual)) { + var visualArr = []; + + if (zrUtil.isObject(visual)) { + each(visual, function (v, cate) { + var index = categoryMap[cate]; + visualArr[index != null ? index : CATEGORY_DEFAULT_VISUAL_INDEX] = v; + }); + } + else { // Is primary type, represents default visual. + visualArr[CATEGORY_DEFAULT_VISUAL_INDEX] = visual; + } + + visual = thisOption.visual = visualArr; + } + + // Remove categories that has no visual, + // then we can mapping them to CATEGORY_DEFAULT_VISUAL_INDEX. + for (var i = categories.length - 1; i >= 0; i--) { + if (visual[i] == null) { + delete categoryMap[categories[i]]; + categories.pop(); + } + } + } + + function normalizeVisualRange(thisOption, isCategory) { + var visual = thisOption.visual; + var visualArr = []; + + if (zrUtil.isObject(visual)) { + each(visual, function (v) { + visualArr.push(v); + }); + } + else if (visual != null) { + visualArr.push(visual); + } + + var doNotNeedPair = {color: 1, symbol: 1}; + + if (!isCategory + && visualArr.length === 1 + && !(thisOption.type in doNotNeedPair) + ) { + // Do not care visualArr.length === 0, which is illegal. + visualArr[1] = visualArr[0]; + } + + thisOption.visual = visualArr; + } + + function makePartialColorVisualHandler(applyValue) { + return { + applyVisual: function (value, getter, setter) { + value = this.mapValueToVisual(value); + // Must not be array value + setter('color', applyValue(getter('color'), value)); + }, + _doMap: makeDoMap([0, 1]) + }; + } + + function doMapToArray(arr, normalized) { + var visual = this.option.visual; + return visual[ + Math.round(linearMap(normalized, [0, 1], [0, visual.length - 1], true)) + ] || {}; + } + + function makeApplyVisual(visualType) { + return function (value, getter, setter) { + setter(visualType, this.mapValueToVisual(value)); + }; + } + + function doMapCategory(normalized) { + var visual = this.option.visual; + return visual[ + (this.option.loop && normalized !== CATEGORY_DEFAULT_VISUAL_INDEX) + ? normalized % visual.length + : normalized + ]; + } + + function doMapFixed() { + return this.option.visual[0]; + } + + function makeDoMap(sourceExtent) { + return { + linear: function (normalized) { + return linearMap(normalized, sourceExtent, this.option.visual, true); + }, + category: doMapCategory, + piecewise: function (normalized, value) { + var result = getSpecifiedVisual.call(this, value); + if (result == null) { + result = linearMap(normalized, sourceExtent, this.option.visual, true); + } + return result; + }, + fixed: doMapFixed + }; + } + + function getSpecifiedVisual(value) { + var thisOption = this.option; + var pieceList = thisOption.pieceList; + if (thisOption.hasSpecialVisual) { + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList); + var piece = pieceList[pieceIndex]; + if (piece && piece.visual) { + return piece.visual[this.type]; + } + } + } + + + /** + * Normalizers by mapping methods. + */ + var normalizers = { + + linear: function (value) { + return linearMap(value, this.option.dataExtent, [0, 1], true); + }, + + piecewise: function (value) { + var pieceList = this.option.pieceList; + var pieceIndex = VisualMapping.findPieceIndex(value, pieceList, true); + if (pieceIndex != null) { + return linearMap(pieceIndex, [0, pieceList.length - 1], [0, 1], true); + } + }, + + category: function (value) { + var index = this.option.categories + ? this.option.categoryMap[value] + : value; // ordinal + return index == null ? CATEGORY_DEFAULT_VISUAL_INDEX : index; + }, + + fixed: zrUtil.noop + }; + + + + /** + * @public + */ + VisualMapping.addVisualHandler = function (name, handler) { + visualHandlers[name] = handler; + }; + + /** + * @public + */ + VisualMapping.isValidType = function (visualType) { + return visualHandlers.hasOwnProperty(visualType); + }; + + /** + * Convinent method. + * Visual can be Object or Array or primary type. + * + * @public + */ + VisualMapping.eachVisual = function (visual, callback, context) { + if (zrUtil.isObject(visual)) { + zrUtil.each(visual, callback, context); + } + else { + callback.call(context, visual); + } + }; + + VisualMapping.mapVisual = function (visual, callback, context) { + var isPrimary; + var newVisual = zrUtil.isArray(visual) + ? [] + : zrUtil.isObject(visual) + ? {} + : (isPrimary = true, null); + + VisualMapping.eachVisual(visual, function (v, key) { + var newVal = callback.call(context, v, key); + isPrimary ? (newVisual = newVal) : (newVisual[key] = newVal); + }); + return newVisual; + }; + + /** + * @public + * @param {Object} obj + * @return {Oject} new object containers visual values. + * If no visuals, return null. + */ + VisualMapping.retrieveVisuals = function (obj) { + var ret = {}; + var hasVisual; + + obj && each(visualHandlers, function (h, visualType) { + if (obj.hasOwnProperty(visualType)) { + ret[visualType] = obj[visualType]; + hasVisual = true; + } + }); + + return hasVisual ? ret : null; + }; + + /** + * Give order to visual types, considering colorSaturation, colorAlpha depends on color. + * + * @public + * @param {(Object|Array)} visualTypes If Object, like: {color: ..., colorSaturation: ...} + * IF Array, like: ['color', 'symbol', 'colorSaturation'] + * @return {Array.} Sorted visual types. + */ + VisualMapping.prepareVisualTypes = function (visualTypes) { + if (isObject(visualTypes)) { + var types = []; + each(visualTypes, function (item, type) { + types.push(type); + }); + visualTypes = types; + } + else if (zrUtil.isArray(visualTypes)) { + visualTypes = visualTypes.slice(); + } + else { + return []; + } + + visualTypes.sort(function (type1, type2) { + // color should be front of colorSaturation, colorAlpha, ... + // symbol and symbolSize do not matter. + return (type2 === 'color' && type1 !== 'color' && type1.indexOf('color') === 0) + ? 1 : -1; + }); + + return visualTypes; + }; + + /** + * 'color', 'colorSaturation', 'colorAlpha', ... are depends on 'color'. + * Other visuals are only depends on themself. + * + * @public + * @param {string} visualType1 + * @param {string} visualType2 + * @return {boolean} + */ + VisualMapping.dependsOn = function (visualType1, visualType2) { + return visualType2 === 'color' + ? !!(visualType1 && visualType1.indexOf(visualType2) === 0) + : visualType1 === visualType2; + }; + + /** + * @param {number} value + * @param {Array.} pieceList [{value: ..., interval: [min, max]}, ...] + * Always from small to big. + * @param {boolean} [findClosestWhenOutside=false] + * @return {number} index + */ + VisualMapping.findPieceIndex = function (value, pieceList, findClosestWhenOutside) { + var possibleI; + var abs = Infinity; + + // value has the higher priority. + for (var i = 0, len = pieceList.length; i < len; i++) { + var pieceValue = pieceList[i].value; + if (pieceValue != null) { + if (pieceValue === value) { + return i; + } + findClosestWhenOutside && updatePossible(pieceValue, i); + } + } + + for (var i = 0, len = pieceList.length; i < len; i++) { + var piece = pieceList[i]; + var interval = piece.interval; + var close = piece.close; + + if (interval) { + if (interval[0] === -Infinity) { + if (littleThan(close[1], value, interval[1])) { + return i; + } + } + else if (interval[1] === Infinity) { + if (littleThan(close[0], interval[0], value)) { + return i; + } + } + else if ( + littleThan(close[0], interval[0], value) + && littleThan(close[1], value, interval[1]) + ) { + return i; + } + findClosestWhenOutside && updatePossible(interval[0], i); + findClosestWhenOutside && updatePossible(interval[1], i); + } + } + + if (findClosestWhenOutside) { + return value === Infinity + ? pieceList.length - 1 + : value === -Infinity + ? 0 + : possibleI; + } + + function updatePossible(val, index) { + var newAbs = Math.abs(val - value); + if (newAbs < abs) { + abs = newAbs; + possibleI = index; + } + } + + }; + + function littleThan(close, a, b) { + return close ? a <= b : a < b; + } + + module.exports = VisualMapping; + + + +/***/ }, +/* 193 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var layout = __webpack_require__(21); + var helper = __webpack_require__(187); + var BoundingRect = __webpack_require__(9); + var helper = __webpack_require__(187); + + var mathMax = Math.max; + var mathMin = Math.min; + var parsePercent = numberUtil.parsePercent; + var retrieveValue = zrUtil.retrieve; + var each = zrUtil.each; + + /** + * @public + */ + function update(ecModel, api, payload) { + // Layout result in each node: + // {x, y, width, height, area, borderWidth} + var condition = {mainType: 'series', subType: 'treemap', query: payload}; + ecModel.eachComponent(condition, function (seriesModel) { + + var ecWidth = api.getWidth(); + var ecHeight = api.getHeight(); + var seriesOption = seriesModel.option; + + var size = seriesOption.size || []; // Compatible with ec2. + var containerWidth = parsePercent( + retrieveValue(seriesOption.width, size[0]), + ecWidth + ); + var containerHeight = parsePercent( + retrieveValue(seriesOption.height, size[1]), + ecHeight + ); + + var layoutInfo = layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + // Fetch payload info. + var payloadType = payload && payload.type; + var targetInfo = helper.retrieveTargetInfo(payload, seriesModel); + var rootRect = (payloadType === 'treemapRender' || payloadType === 'treemapMove') + ? payload.rootRect : null; + var viewRoot = seriesModel.getViewRoot(); + var viewAbovePath = helper.getPathToRoot(viewRoot); + + if (payloadType !== 'treemapMove') { + var rootSize = payloadType === 'treemapZoomToNode' + ? estimateRootSize( + seriesModel, targetInfo, viewRoot, containerWidth, containerHeight + ) + : rootRect + ? [rootRect.width, rootRect.height] + : [containerWidth, containerHeight]; + + var sort = seriesOption.sort; + if (sort && sort !== 'asc' && sort !== 'desc') { + sort = 'desc'; + } + var options = { + squareRatio: seriesOption.squareRatio, + sort: sort, + leafDepth: seriesOption.leafDepth + }; + + // layout should be cleared because using updateView but not update. + viewRoot.hostTree.clearLayouts(); + + // TODO + // optimize: if out of view clip, do not layout. + // But take care that if do not render node out of view clip, + // how to calculate start po + + var viewRootLayout = { + x: 0, y: 0, + width: rootSize[0], height: rootSize[1], + area: rootSize[0] * rootSize[1] + }; + viewRoot.setLayout(viewRootLayout); + + squarify(viewRoot, options, false, 0); + // Supplement layout. + var viewRootLayout = viewRoot.getLayout(); + each(viewAbovePath, function (node, index) { + var childValue = (viewAbovePath[index + 1] || viewRoot).getValue(); + node.setLayout(zrUtil.extend( + {dataExtent: [childValue, childValue], borderWidth: 0}, + viewRootLayout + )); + }); + } + + var treeRoot = seriesModel.getData().tree.root; + + treeRoot.setLayout( + calculateRootPosition(layoutInfo, rootRect, targetInfo), + true + ); + + seriesModel.setLayoutInfo(layoutInfo); + + // FIXME + // 现在没有clip功能,暂时取ec高宽。 + prunning( + treeRoot, + // Transform to base element coordinate system. + new BoundingRect(-layoutInfo.x, -layoutInfo.y, ecWidth, ecHeight), + viewAbovePath, + viewRoot, + 0 + ); + }); + } + + /** + * Layout treemap with squarify algorithm. + * @see https://graphics.ethz.ch/teaching/scivis_common/Literature/squarifiedTreeMaps.pdf + * @see https://github.com/mbostock/d3/blob/master/src/layout/treemap.js + * + * @protected + * @param {module:echarts/data/Tree~TreeNode} node + * @param {Object} options + * @param {string} options.sort 'asc' or 'desc' + * @param {number} options.squareRatio + * @param {boolean} hideChildren + * @param {number} depth + */ + function squarify(node, options, hideChildren, depth) { + var width; + var height; + + if (node.isRemoved()) { + return; + } + + var thisLayout = node.getLayout(); + width = thisLayout.width; + height = thisLayout.height; + + // Considering border and gap + var itemStyleModel = node.getModel('itemStyle.normal'); + var borderWidth = itemStyleModel.get('borderWidth'); + var halfGapWidth = itemStyleModel.get('gapWidth') / 2; + var layoutOffset = borderWidth - halfGapWidth; + var nodeModel = node.getModel(); + + node.setLayout({borderWidth: borderWidth}, true); + + width = mathMax(width - 2 * layoutOffset, 0); + height = mathMax(height - 2 * layoutOffset, 0); + + var totalArea = width * height; + var viewChildren = initChildren( + node, nodeModel, totalArea, options, hideChildren, depth + ); + + if (!viewChildren.length) { + return; + } + + var rect = {x: layoutOffset, y: layoutOffset, width: width, height: height}; + var rowFixedLength = mathMin(width, height); + var best = Infinity; // the best row score so far + var row = []; + row.area = 0; + + for (var i = 0, len = viewChildren.length; i < len;) { + var child = viewChildren[i]; + + row.push(child); + row.area += child.getLayout().area; + var score = worst(row, rowFixedLength, options.squareRatio); + + // continue with this orientation + if (score <= best) { + i++; + best = score; + } + // abort, and try a different orientation + else { + row.area -= row.pop().getLayout().area; + position(row, rowFixedLength, rect, halfGapWidth, false); + rowFixedLength = mathMin(rect.width, rect.height); + row.length = row.area = 0; + best = Infinity; + } + } + + if (row.length) { + position(row, rowFixedLength, rect, halfGapWidth, true); + } + + if (!hideChildren) { + var childrenVisibleMin = nodeModel.get('childrenVisibleMin'); + if (childrenVisibleMin != null && totalArea < childrenVisibleMin) { + hideChildren = true; + } + } + + for (var i = 0, len = viewChildren.length; i < len; i++) { + squarify(viewChildren[i], options, hideChildren, depth + 1); + } + } + + /** + * Set area to each child, and calculate data extent for visual coding. + */ + function initChildren(node, nodeModel, totalArea, options, hideChildren, depth) { + var viewChildren = node.children || []; + var orderBy = options.sort; + orderBy !== 'asc' && orderBy !== 'desc' && (orderBy = null); + + var overLeafDepth = options.leafDepth != null && options.leafDepth <= depth; + + // leafDepth has higher priority. + if (hideChildren && !overLeafDepth) { + return (node.viewChildren = []); + } + + // Sort children, order by desc. + viewChildren = zrUtil.filter(viewChildren, function (child) { + return !child.isRemoved(); + }); + + sort(viewChildren, orderBy); + + var info = statistic(nodeModel, viewChildren, orderBy); + + if (info.sum === 0) { + return (node.viewChildren = []); + } + + info.sum = filterByThreshold(nodeModel, totalArea, info.sum, orderBy, viewChildren); + + if (info.sum === 0) { + return (node.viewChildren = []); + } + + // Set area to each child. + for (var i = 0, len = viewChildren.length; i < len; i++) { + var area = viewChildren[i].getValue() / info.sum * totalArea; + // Do not use setLayout({...}, true), because it is needed to clear last layout. + viewChildren[i].setLayout({area: area}); + } + + if (overLeafDepth) { + viewChildren.length && node.setLayout({isLeafRoot: true}, true); + viewChildren.length = 0; + } + + node.viewChildren = viewChildren; + node.setLayout({dataExtent: info.dataExtent}, true); + + return viewChildren; + } + + /** + * Consider 'visibleMin'. Modify viewChildren and get new sum. + */ + function filterByThreshold(nodeModel, totalArea, sum, orderBy, orderedChildren) { + + // visibleMin is not supported yet when no option.sort. + if (!orderBy) { + return sum; + } + + var visibleMin = nodeModel.get('visibleMin'); + var len = orderedChildren.length; + var deletePoint = len; + + // Always travel from little value to big value. + for (var i = len - 1; i >= 0; i--) { + var value = orderedChildren[ + orderBy === 'asc' ? len - i - 1 : i + ].getValue(); + + if (value / sum * totalArea < visibleMin) { + deletePoint = i; + sum -= value; + } + } + + orderBy === 'asc' + ? orderedChildren.splice(0, len - deletePoint) + : orderedChildren.splice(deletePoint, len - deletePoint); + + return sum; + } + + /** + * Sort + */ + function sort(viewChildren, orderBy) { + if (orderBy) { + viewChildren.sort(function (a, b) { + return orderBy === 'asc' + ? a.getValue() - b.getValue() : b.getValue() - a.getValue(); + }); + } + return viewChildren; + } + + /** + * Statistic + */ + function statistic(nodeModel, children, orderBy) { + // Calculate sum. + var sum = 0; + for (var i = 0, len = children.length; i < len; i++) { + sum += children[i].getValue(); + } + + // Statistic data extent for latter visual coding. + // Notice: data extent should be calculate based on raw children + // but not filtered view children, otherwise visual mapping will not + // be stable when zoom (where children is filtered by visibleMin). + + var dimension = nodeModel.get('visualDimension'); + var dataExtent; + + // The same as area dimension. + if (!children || !children.length) { + dataExtent = [NaN, NaN]; + } + else if (dimension === 'value' && orderBy) { + dataExtent = [ + children[children.length - 1].getValue(), + children[0].getValue() + ]; + orderBy === 'asc' && dataExtent.reverse(); + } + // Other dimension. + else { + var dataExtent = [Infinity, -Infinity]; + each(children, function (child) { + var value = child.getValue(dimension); + value < dataExtent[0] && (dataExtent[0] = value); + value > dataExtent[1] && (dataExtent[1] = value); + }); + } + + return {sum: sum, dataExtent: dataExtent}; + } + + /** + * Computes the score for the specified row, + * as the worst aspect ratio. + */ + function worst(row, rowFixedLength, ratio) { + var areaMax = 0; + var areaMin = Infinity; + + for (var i = 0, area, len = row.length; i < len; i++) { + area = row[i].getLayout().area; + if (area) { + area < areaMin && (areaMin = area); + area > areaMax && (areaMax = area); + } + } + + var squareArea = row.area * row.area; + var f = rowFixedLength * rowFixedLength * ratio; + + return squareArea + ? mathMax( + (f * areaMax) / squareArea, + squareArea / (f * areaMin) + ) + : Infinity; + } + + /** + * Positions the specified row of nodes. Modifies `rect`. + */ + function position(row, rowFixedLength, rect, halfGapWidth, flush) { + // When rowFixedLength === rect.width, + // it is horizontal subdivision, + // rowFixedLength is the width of the subdivision, + // rowOtherLength is the height of the subdivision, + // and nodes will be positioned from left to right. + + // wh[idx0WhenH] means: when horizontal, + // wh[idx0WhenH] => wh[0] => 'width'. + // xy[idx1WhenH] => xy[1] => 'y'. + var idx0WhenH = rowFixedLength === rect.width ? 0 : 1; + var idx1WhenH = 1 - idx0WhenH; + var xy = ['x', 'y']; + var wh = ['width', 'height']; + + var last = rect[xy[idx0WhenH]]; + var rowOtherLength = rowFixedLength + ? row.area / rowFixedLength : 0; + + if (flush || rowOtherLength > rect[wh[idx1WhenH]]) { + rowOtherLength = rect[wh[idx1WhenH]]; // over+underflow + } + for (var i = 0, rowLen = row.length; i < rowLen; i++) { + var node = row[i]; + var nodeLayout = {}; + var step = rowOtherLength + ? node.getLayout().area / rowOtherLength : 0; + + var wh1 = nodeLayout[wh[idx1WhenH]] = mathMax(rowOtherLength - 2 * halfGapWidth, 0); + + // We use Math.max/min to avoid negative width/height when considering gap width. + var remain = rect[xy[idx0WhenH]] + rect[wh[idx0WhenH]] - last; + var modWH = (i === rowLen - 1 || remain < step) ? remain : step; + var wh0 = nodeLayout[wh[idx0WhenH]] = mathMax(modWH - 2 * halfGapWidth, 0); + + nodeLayout[xy[idx1WhenH]] = rect[xy[idx1WhenH]] + mathMin(halfGapWidth, wh1 / 2); + nodeLayout[xy[idx0WhenH]] = last + mathMin(halfGapWidth, wh0 / 2); + + last += modWH; + node.setLayout(nodeLayout, true); + } + + rect[xy[idx1WhenH]] += rowOtherLength; + rect[wh[idx1WhenH]] -= rowOtherLength; + } + + // Return [containerWidth, containerHeight] as defualt. + function estimateRootSize(seriesModel, targetInfo, viewRoot, containerWidth, containerHeight) { + // If targetInfo.node exists, we zoom to the node, + // so estimate whold width and heigth by target node. + var currNode = (targetInfo || {}).node; + var defaultSize = [containerWidth, containerHeight]; + + if (!currNode || currNode === viewRoot) { + return defaultSize; + } + + var parent; + var viewArea = containerWidth * containerHeight; + var area = viewArea * seriesModel.option.zoomToNodeRatio; + + while (parent = currNode.parentNode) { // jshint ignore:line + var sum = 0; + var siblings = parent.children; + + for (var i = 0, len = siblings.length; i < len; i++) { + sum += siblings[i].getValue(); + } + var currNodeValue = currNode.getValue(); + if (currNodeValue === 0) { + return defaultSize; + } + area *= sum / currNodeValue; + + var borderWidth = parent.getModel('itemStyle.normal').get('borderWidth'); + + if (isFinite(borderWidth)) { + // Considering border, suppose aspect ratio is 1. + area += 4 * borderWidth * borderWidth + 4 * borderWidth * Math.pow(area, 0.5); + } + + area > numberUtil.MAX_SAFE_INTEGER && (area = numberUtil.MAX_SAFE_INTEGER); + + currNode = parent; + } + + area < viewArea && (area = viewArea); + var scale = Math.pow(area / viewArea, 0.5); + + return [containerWidth * scale, containerHeight * scale]; + } + + // Root postion base on coord of containerGroup + function calculateRootPosition(layoutInfo, rootRect, targetInfo) { + if (rootRect) { + return {x: rootRect.x, y: rootRect.y}; + } + + var defaultPosition = {x: 0, y: 0}; + if (!targetInfo) { + return defaultPosition; + } + + // If targetInfo is fetched by 'retrieveTargetInfo', + // old tree and new tree are the same tree, + // so the node still exists and we can visit it. + + var targetNode = targetInfo.node; + var layout = targetNode.getLayout(); + + if (!layout) { + return defaultPosition; + } + + // Transform coord from local to container. + var targetCenter = [layout.width / 2, layout.height / 2]; + var node = targetNode; + while (node) { + var nodeLayout = node.getLayout(); + targetCenter[0] += nodeLayout.x; + targetCenter[1] += nodeLayout.y; + node = node.parentNode; + } + + return { + x: layoutInfo.width / 2 - targetCenter[0], + y: layoutInfo.height / 2 - targetCenter[1] + }; + } + + // Mark nodes visible for prunning when visual coding and rendering. + // Prunning depends on layout and root position, so we have to do it after layout. + function prunning(node, clipRect, viewAbovePath, viewRoot, depth) { + var nodeLayout = node.getLayout(); + var nodeInViewAbovePath = viewAbovePath[depth]; + var isAboveViewRoot = nodeInViewAbovePath && nodeInViewAbovePath === node; + + if ( + (nodeInViewAbovePath && !isAboveViewRoot) + || (depth === viewAbovePath.length && node !== viewRoot) + ) { + return; + } + + node.setLayout({ + // isInView means: viewRoot sub tree + viewAbovePath + isInView: true, + // invisible only means: outside view clip so that the node can not + // see but still layout for animation preparation but not render. + invisible: !isAboveViewRoot && !clipRect.intersect(nodeLayout), + isAboveViewRoot: isAboveViewRoot + }, true); + + // Transform to child coordinate. + var childClipRect = new BoundingRect( + clipRect.x - nodeLayout.x, + clipRect.y - nodeLayout.y, + clipRect.width, + clipRect.height + ); + + each(node.viewChildren || [], function (child) { + prunning(child, childClipRect, viewAbovePath, viewRoot, depth + 1); + }); + } + + module.exports = update; + + +/***/ }, +/* 194 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + __webpack_require__(195); + __webpack_require__(198); + + __webpack_require__(203); + + echarts.registerProcessor(__webpack_require__(204)); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'graph', 'circle', null + )); + echarts.registerVisual(__webpack_require__(205)); + echarts.registerVisual(__webpack_require__(206)); + + echarts.registerLayout(__webpack_require__(207)); + echarts.registerLayout(__webpack_require__(210)); + echarts.registerLayout(__webpack_require__(212)); + + // Graph view coordinate system + echarts.registerCoordinateSystem('graphView', { + create: __webpack_require__(214) + }); + + +/***/ }, +/* 195 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + var Model = __webpack_require__(12); + + var createGraphFromNodeEdge = __webpack_require__(196); + + var GraphSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.graph', + + init: function (option) { + GraphSeries.superApply(this, 'init', arguments); + + // Provide data for legend select + this.legendDataProvider = function () { + return this._categoriesData; + }; + + this.fillDataTextStyle(option.edges || option.links); + + this._updateCategoriesData(); + }, + + mergeOption: function (option) { + GraphSeries.superApply(this, 'mergeOption', arguments); + + this.fillDataTextStyle(option.edges || option.links); + + this._updateCategoriesData(); + }, + + mergeDefaultAndTheme: function (option) { + GraphSeries.superApply(this, 'mergeDefaultAndTheme', arguments); + modelUtil.defaultEmphasis(option.edgeLabel, modelUtil.LABEL_OPTIONS); + }, + + getInitialData: function (option, ecModel) { + var edges = option.edges || option.links || []; + var nodes = option.data || option.nodes || []; + var self = this; + + if (nodes && edges) { + return createGraphFromNodeEdge(nodes, edges, this, true, beforeLink).data; + } + + function beforeLink(nodeData, edgeData) { + // Overwrite nodeData.getItemModel to + nodeData.wrapMethod('getItemModel', function (model) { + var categoriesModels = self._categoriesModels; + var categoryIdx = model.getShallow('category'); + var categoryModel = categoriesModels[categoryIdx]; + if (categoryModel) { + categoryModel.parentModel = model.parentModel; + model.parentModel = categoryModel; + } + return model; + }); + + var edgeLabelModel = self.getModel('edgeLabel'); + var wrappedGetEdgeModel = function (path, parentModel) { + var pathArr = (path || '').split('.'); + if (pathArr[0] === 'label') { + parentModel = parentModel + || edgeLabelModel.getModel(pathArr.slice(1)); + } + var model = Model.prototype.getModel.call(this, pathArr, parentModel); + model.getModel = wrappedGetEdgeModel; + return model; + }; + edgeData.wrapMethod('getItemModel', function (model) { + // FIXME Wrap get method ? + model.getModel = wrappedGetEdgeModel; + return model; + }); + } + }, + + /** + * @return {module:echarts/data/Graph} + */ + getGraph: function () { + return this.getData().graph; + }, + + /** + * @return {module:echarts/data/List} + */ + getEdgeData: function () { + return this.getGraph().edgeData; + }, + + /** + * @return {module:echarts/data/List} + */ + getCategoriesData: function () { + return this._categoriesData; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + if (dataType === 'edge') { + var nodeData = this.getData(); + var params = this.getDataParams(dataIndex, dataType); + var edge = nodeData.graph.getEdgeByIndex(dataIndex); + var sourceName = nodeData.getName(edge.node1.dataIndex); + var targetName = nodeData.getName(edge.node2.dataIndex); + var html = sourceName + ' > ' + targetName; + if (params.value) { + html += ' : ' + params.value; + } + return html; + } + else { // dataType === 'node' or empty + return GraphSeries.superApply(this, 'formatTooltip', arguments); + } + }, + + _updateCategoriesData: function () { + var categories = zrUtil.map(this.option.categories || [], function (category) { + // Data must has value + return category.value != null ? category : zrUtil.extend({ + value: 0 + }, category); + }); + var categoriesData = new List(['value'], this); + categoriesData.initData(categories); + + this._categoriesData = categoriesData; + + this._categoriesModels = categoriesData.mapArray(function (idx) { + return categoriesData.getItemModel(idx, true); + }); + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + }, + + ifEnableAnimation: function () { + return GraphSeries.superCall(this, 'ifEnableAnimation') + // Not enable animation when do force layout + && !(this.get('layout') === 'force' && this.get('force.layoutAnimation')); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + coordinateSystem: 'view', + + // Default option for all coordinate systems + // xAxisIndex: 0, + // yAxisIndex: 0, + // polarIndex: 0, + // geoIndex: 0, + + legendHoverLink: true, + + hoverAnimation: true, + + layout: null, + + focusNodeAdjacency: false, + + // Configuration of circular layout + circular: { + rotateLabel: false + }, + // Configuration of force directed layout + force: { + initLayout: null, + // Node repulsion. Can be an array to represent range. + repulsion: [0, 50], + gravity: 0.1, + + // Edge length. Can be an array to represent range. + edgeLength: 30, + + layoutAnimation: true + }, + + left: 'center', + top: 'center', + // right: null, + // bottom: null, + // width: '80%', + // height: '80%', + + symbol: 'circle', + symbolSize: 10, + + edgeSymbol: ['none', 'none'], + edgeSymbolSize: 10, + edgeLabel: { + normal: { + position: 'middle' + }, + emphasis: {} + }, + + draggable: false, + + roam: false, + + // Default on center of graph + center: null, + + zoom: 1, + // Symbol size scale ratio in roam + nodeScaleRatio: 0.6, + + // categories: [], + + // data: [] + // Or + // nodes: [] + // + // links: [] + // Or + // edges: [] + + label: { + normal: { + show: false, + formatter: '{b}' + }, + emphasis: { + show: true + } + }, + + itemStyle: { + normal: {}, + emphasis: {} + }, + + lineStyle: { + normal: { + color: '#aaa', + width: 1, + curveness: 0, + opacity: 0.5 + }, + emphasis: {} + } + } + }); + + module.exports = GraphSeries; + + +/***/ }, +/* 196 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var Graph = __webpack_require__(197); + var linkList = __webpack_require__(185); + var completeDimensions = __webpack_require__(102); + var CoordinateSystem = __webpack_require__(26); + var zrUtil = __webpack_require__(4); + var createListFromArray = __webpack_require__(101); + + module.exports = function (nodes, edges, hostModel, directed, beforeLink) { + var graph = new Graph(directed); + for (var i = 0; i < nodes.length; i++) { + graph.addNode(zrUtil.retrieve( + // Id, name, dataIndex + nodes[i].id, nodes[i].name, i + ), i); + } + + var linkNameList = []; + var validEdges = []; + var linkCount = 0; + for (var i = 0; i < edges.length; i++) { + var link = edges[i]; + var source = link.source; + var target = link.target; + // addEdge may fail when source or target not exists + if (graph.addEdge(source, target, linkCount)) { + validEdges.push(link); + linkNameList.push(zrUtil.retrieve(link.id, source + ' > ' + target)); + linkCount++; + } + } + + var coordSys = hostModel.get('coordinateSystem'); + var nodeData; + if (coordSys === 'cartesian2d' || coordSys === 'polar') { + nodeData = createListFromArray(nodes, hostModel, hostModel.ecModel); + } + else { + // FIXME + var coordSysCtor = CoordinateSystem.get(coordSys); + // FIXME + var dimensionNames = completeDimensions( + ((coordSysCtor && coordSysCtor.type !== 'view') ? (coordSysCtor.dimensions || []) : []).concat(['value']), + nodes + ); + nodeData = new List(dimensionNames, hostModel); + nodeData.initData(nodes); + } + + var edgeData = new List(['value'], hostModel); + edgeData.initData(validEdges, linkNameList); + + beforeLink && beforeLink(nodeData, edgeData); + + linkList({ + mainData: nodeData, + struct: graph, + structAttr: 'graph', + datas: {node: nodeData, edge: edgeData}, + datasAttr: {node: 'data', edge: 'edgeData'} + }); + + // Update dataIndex of nodes and edges because invalid edge may be removed + graph.update(); + + return graph; + }; + + +/***/ }, +/* 197 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * Graph data structure + * + * @module echarts/data/Graph + * @author Yi Shen(https://www.github.com/pissang) + */ + + + var zrUtil = __webpack_require__(4); + + /** + * @alias module:echarts/data/Graph + * @constructor + * @param {boolean} directed + */ + var Graph = function(directed) { + /** + * 是否是有向图 + * @type {boolean} + * @private + */ + this._directed = directed || false; + + /** + * @type {Array.} + * @readOnly + */ + this.nodes = []; + + /** + * @type {Array.} + * @readOnly + */ + this.edges = []; + + /** + * @type {Object.} + * @private + */ + this._nodesMap = {}; + /** + * @type {Object.} + * @private + */ + this._edgesMap = {}; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.data; + + /** + * @type {module:echarts/data/List} + * @readOnly + */ + this.edgeData; + }; + + var graphProto = Graph.prototype; + /** + * @type {string} + */ + graphProto.type = 'graph'; + + /** + * If is directed graph + * @return {boolean} + */ + graphProto.isDirected = function () { + return this._directed; + }; + + /** + * Add a new node + * @param {string} id + * @param {number} [dataIndex] + */ + graphProto.addNode = function (id, dataIndex) { + id = id || ('' + dataIndex); + + var nodesMap = this._nodesMap; + + if (nodesMap[id]) { + return; + } + + var node = new Node(id, dataIndex); + node.hostGraph = this; + + this.nodes.push(node); + + nodesMap[id] = node; + return node; + }; + + /** + * Get node by data index + * @param {number} dataIndex + * @return {module:echarts/data/Graph~Node} + */ + graphProto.getNodeByIndex = function (dataIndex) { + var rawIdx = this.data.getRawIndex(dataIndex); + return this.nodes[rawIdx]; + }; + /** + * Get node by id + * @param {string} id + * @return {module:echarts/data/Graph.Node} + */ + graphProto.getNodeById = function (id) { + return this._nodesMap[id]; + }; + + /** + * Add a new edge + * @param {number|string|module:echarts/data/Graph.Node} n1 + * @param {number|string|module:echarts/data/Graph.Node} n2 + * @param {number} [dataIndex=-1] + * @return {module:echarts/data/Graph.Edge} + */ + graphProto.addEdge = function (n1, n2, dataIndex) { + var nodesMap = this._nodesMap; + var edgesMap = this._edgesMap; + + // PNEDING + if (typeof n1 === 'number') { + n1 = this.nodes[n1]; + } + if (typeof n2 === 'number') { + n2 = this.nodes[n2]; + } + + if (!(n1 instanceof Node)) { + n1 = nodesMap[n1]; + } + if (!(n2 instanceof Node)) { + n2 = nodesMap[n2]; + } + if (!n1 || !n2) { + return; + } + + var key = n1.id + '-' + n2.id; + // PENDING + if (edgesMap[key]) { + return; + } + + var edge = new Edge(n1, n2, dataIndex); + edge.hostGraph = this; + + if (this._directed) { + n1.outEdges.push(edge); + n2.inEdges.push(edge); + } + n1.edges.push(edge); + if (n1 !== n2) { + n2.edges.push(edge); + } + + this.edges.push(edge); + edgesMap[key] = edge; + + return edge; + }; + + /** + * Get edge by data index + * @param {number} dataIndex + * @return {module:echarts/data/Graph~Node} + */ + graphProto.getEdgeByIndex = function (dataIndex) { + var rawIdx = this.edgeData.getRawIndex(dataIndex); + return this.edges[rawIdx]; + }; + /** + * Get edge by two linked nodes + * @param {module:echarts/data/Graph.Node|string} n1 + * @param {module:echarts/data/Graph.Node|string} n2 + * @return {module:echarts/data/Graph.Edge} + */ + graphProto.getEdge = function (n1, n2) { + if (n1 instanceof Node) { + n1 = n1.id; + } + if (n2 instanceof Node) { + n2 = n2.id; + } + + var edgesMap = this._edgesMap; + + if (this._directed) { + return edgesMap[n1 + '-' + n2]; + } else { + return edgesMap[n1 + '-' + n2] + || edgesMap[n2 + '-' + n1]; + } + }; + + /** + * Iterate all nodes + * @param {Function} cb + * @param {*} [context] + */ + graphProto.eachNode = function (cb, context) { + var nodes = this.nodes; + var len = nodes.length; + for (var i = 0; i < len; i++) { + if (nodes[i].dataIndex >= 0) { + cb.call(context, nodes[i], i); + } + } + }; + + /** + * Iterate all edges + * @param {Function} cb + * @param {*} [context] + */ + graphProto.eachEdge = function (cb, context) { + var edges = this.edges; + var len = edges.length; + for (var i = 0; i < len; i++) { + if (edges[i].dataIndex >= 0 + && edges[i].node1.dataIndex >= 0 + && edges[i].node2.dataIndex >= 0 + ) { + cb.call(context, edges[i], i); + } + } + }; + + /** + * Breadth first traverse + * @param {Function} cb + * @param {module:echarts/data/Graph.Node} startNode + * @param {string} [direction='none'] 'none'|'in'|'out' + * @param {*} [context] + */ + graphProto.breadthFirstTraverse = function ( + cb, startNode, direction, context + ) { + if (!(startNode instanceof Node)) { + startNode = this._nodesMap[startNode]; + } + if (!startNode) { + return; + } + + var edgeType = direction === 'out' + ? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges'); + + for (var i = 0; i < this.nodes.length; i++) { + this.nodes[i].__visited = false; + } + + if (cb.call(context, startNode, null)) { + return; + } + + var queue = [startNode]; + while (queue.length) { + var currentNode = queue.shift(); + var edges = currentNode[edgeType]; + + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + var otherNode = e.node1 === currentNode + ? e.node2 : e.node1; + if (!otherNode.__visited) { + if (cb.call(otherNode, otherNode, currentNode)) { + // Stop traversing + return; + } + queue.push(otherNode); + otherNode.__visited = true; + } + } + } + }; + + // TODO + // graphProto.depthFirstTraverse = function ( + // cb, startNode, direction, context + // ) { + + // }; + + // Filter update + graphProto.update = function () { + var data = this.data; + var edgeData = this.edgeData; + var nodes = this.nodes; + var edges = this.edges; + + for (var i = 0, len = nodes.length; i < len; i++) { + nodes[i].dataIndex = -1; + } + for (var i = 0, len = data.count(); i < len; i++) { + nodes[data.getRawIndex(i)].dataIndex = i; + } + + edgeData.filterSelf(function (idx) { + var edge = edges[edgeData.getRawIndex(idx)]; + return edge.node1.dataIndex >= 0 && edge.node2.dataIndex >= 0; + }); + + // Update edge + for (var i = 0, len = edges.length; i < len; i++) { + edges[i].dataIndex = -1; + } + for (var i = 0, len = edgeData.count(); i < len; i++) { + edges[edgeData.getRawIndex(i)].dataIndex = i; + } + }; + + /** + * @return {module:echarts/data/Graph} + */ + graphProto.clone = function () { + var graph = new Graph(this._directed); + var nodes = this.nodes; + var edges = this.edges; + for (var i = 0; i < nodes.length; i++) { + graph.addNode(nodes[i].id, nodes[i].dataIndex); + } + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + graph.addEdge(e.node1.id, e.node2.id, e.dataIndex); + } + return graph; + }; + + + /** + * @alias module:echarts/data/Graph.Node + */ + function Node(id, dataIndex) { + /** + * @type {string} + */ + this.id = id == null ? '' : id; + + /** + * @type {Array.} + */ + this.inEdges = []; + /** + * @type {Array.} + */ + this.outEdges = []; + /** + * @type {Array.} + */ + this.edges = []; + /** + * @type {module:echarts/data/Graph} + */ + this.hostGraph; + + /** + * @type {number} + */ + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + + Node.prototype = { + + constructor: Node, + + /** + * @return {number} + */ + degree: function () { + return this.edges.length; + }, + + /** + * @return {number} + */ + inDegree: function () { + return this.inEdges.length; + }, + + /** + * @return {number} + */ + outDegree: function () { + return this.outEdges.length; + }, + + /** + * @param {string} [path] + * @return {module:echarts/model/Model} + */ + getModel: function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.data.getItemModel(this.dataIndex); + + return itemModel.getModel(path); + } + }; + + /** + * 图边 + * @alias module:echarts/data/Graph.Edge + * @param {module:echarts/data/Graph.Node} n1 + * @param {module:echarts/data/Graph.Node} n2 + * @param {number} [dataIndex=-1] + */ + function Edge(n1, n2, dataIndex) { + + /** + * 节点1,如果是有向图则为源节点 + * @type {module:echarts/data/Graph.Node} + */ + this.node1 = n1; + + /** + * 节点2,如果是有向图则为目标节点 + * @type {module:echarts/data/Graph.Node} + */ + this.node2 = n2; + + this.dataIndex = dataIndex == null ? -1 : dataIndex; + } + + /** + * @param {string} [path] + * @return {module:echarts/model/Model} + */ + Edge.prototype.getModel = function (path) { + if (this.dataIndex < 0) { + return; + } + var graph = this.hostGraph; + var itemModel = graph.edgeData.getItemModel(this.dataIndex); + + return itemModel.getModel(path); + }; + + var createGraphDataProxyMixin = function (hostName, dataName) { + return { + /** + * @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'. + * @return {number} + */ + getValue: function (dimension) { + var data = this[hostName][dataName]; + return data.get(data.getDimension(dimension || 'value'), this.dataIndex); + }, + + /** + * @param {Object|string} key + * @param {*} [value] + */ + setVisual: function (key, value) { + this.dataIndex >= 0 + && this[hostName][dataName].setItemVisual(this.dataIndex, key, value); + }, + + /** + * @param {string} key + * @return {boolean} + */ + getVisual: function (key, ignoreParent) { + return this[hostName][dataName].getItemVisual(this.dataIndex, key, ignoreParent); + }, + + /** + * @param {Object} layout + * @return {boolean} [merge=false] + */ + setLayout: function (layout, merge) { + this.dataIndex >= 0 + && this[hostName][dataName].setItemLayout(this.dataIndex, layout, merge); + }, + + /** + * @return {Object} + */ + getLayout: function () { + return this[hostName][dataName].getItemLayout(this.dataIndex); + }, + + /** + * @return {module:zrender/Element} + */ + getGraphicEl: function () { + return this[hostName][dataName].getItemGraphicEl(this.dataIndex); + }, + + /** + * @return {number} + */ + getRawIndex: function () { + return this[hostName][dataName].getRawIndex(this.dataIndex); + } + }; + }; + + zrUtil.mixin(Node, createGraphDataProxyMixin('hostGraph', 'data')); + zrUtil.mixin(Edge, createGraphDataProxyMixin('hostGraph', 'edgeData')); + + Graph.Node = Node; + Graph.Edge = Edge; + + module.exports = Graph; + + +/***/ }, +/* 198 */ +/***/ function(module, exports, __webpack_require__) { + + + + + var SymbolDraw = __webpack_require__(104); + var LineDraw = __webpack_require__(199); + var RoamController = __webpack_require__(174); + + var graphic = __webpack_require__(43); + var adjustEdge = __webpack_require__(202); + var zrUtil = __webpack_require__(4); + + var nodeOpacityPath = ['itemStyle', 'normal', 'opacity']; + var lineOpacityPath = ['lineStyle', 'normal', 'opacity']; + + function getItemOpacity(item, opacityPath) { + return item.getVisual('opacity') || item.getModel().get(opacityPath); + } + + __webpack_require__(1).extendChartView({ + + type: 'graph', + + init: function (ecModel, api) { + var symbolDraw = new SymbolDraw(); + var lineDraw = new LineDraw(); + var group = this.group; + + var controller = new RoamController(api.getZr(), group); + + group.add(symbolDraw.group); + group.add(lineDraw.group); + + this._symbolDraw = symbolDraw; + this._lineDraw = lineDraw; + this._controller = controller; + + this._firstRender = true; + }, + + render: function (seriesModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + + this._model = seriesModel; + this._nodeScaleRatio = seriesModel.get('nodeScaleRatio'); + + var symbolDraw = this._symbolDraw; + var lineDraw = this._lineDraw; + + var group = this.group; + + if (coordSys.type === 'view') { + var groupNewProp = { + position: coordSys.position, + scale: coordSys.scale + }; + if (this._firstRender) { + group.attr(groupNewProp); + } + else { + graphic.updateProps(group, groupNewProp, seriesModel); + } + } + // Fix edge contact point with node + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + + var data = seriesModel.getData(); + symbolDraw.updateData(data); + + var edgeData = seriesModel.getEdgeData(); + lineDraw.updateData(edgeData); + + this._updateNodeAndLinkScale(); + + this._updateController(seriesModel, api); + + clearTimeout(this._layoutTimeout); + var forceLayout = seriesModel.forceLayout; + var layoutAnimation = seriesModel.get('force.layoutAnimation'); + if (forceLayout) { + this._startForceLayoutIteration(forceLayout, layoutAnimation); + } + data.eachItemGraphicEl(function (el, idx) { + var itemModel = data.getItemModel(idx); + // Update draggable + el.off('drag').off('dragend'); + var draggable = data.getItemModel(idx).get('draggable'); + if (draggable) { + el.on('drag', function () { + if (forceLayout) { + forceLayout.warmUp(); + !this._layouting + && this._startForceLayoutIteration(forceLayout, layoutAnimation); + forceLayout.setFixed(idx); + // Write position back to layout + data.setItemLayout(idx, el.position); + } + }, this).on('dragend', function () { + if (forceLayout) { + forceLayout.setUnfixed(idx); + } + }, this); + } + el.setDraggable(draggable && forceLayout); + + el.off('mouseover', this._focusNodeAdjacency); + el.off('mouseout', this._unfocusAll); + if (itemModel.get('focusNodeAdjacency')) { + el.on('mouseover', this._focusNodeAdjacency, this); + el.on('mouseout', this._unfocusAll, this); + } + }, this); + + var circularRotateLabel = seriesModel.get('layout') === 'circular' && seriesModel.get('circular.rotateLabel'); + var cx = data.getLayout('cx'); + var cy = data.getLayout('cy'); + data.eachItemGraphicEl(function (el, idx) { + var symbolPath = el.getSymbolPath(); + if (circularRotateLabel) { + var pos = data.getItemLayout(idx); + var rad = Math.atan2(pos[1] - cy, pos[0] - cx); + if (rad < 0) { + rad = Math.PI * 2 + rad; + } + var isLeft = pos[0] < cx; + if (isLeft) { + rad = rad - Math.PI; + } + var textPosition = isLeft ? 'left' : 'right'; + symbolPath.setStyle({ + textRotation: rad, + textPosition: textPosition + }); + symbolPath.hoverStyle && (symbolPath.hoverStyle.textPosition = textPosition); + } + else { + symbolPath.setStyle({ + textRotation: 0 + }); + } + }); + + this._firstRender = false; + }, + + _focusNodeAdjacency: function (e) { + var data = this._model.getData(); + var graph = data.graph; + var el = e.target; + var dataIndex = el.dataIndex; + var dataType = el.dataType; + + function fadeOutItem(item, opacityPath) { + var opacity = getItemOpacity(item, opacityPath); + var el = item.getGraphicEl(); + if (opacity == null) { + opacity = 1; + } + + el.traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity * 0.1); + } + }); + } + + function fadeInItem(item, opacityPath) { + var opacity = getItemOpacity(item, opacityPath); + var el = item.getGraphicEl(); + + el.traverse(function (child) { + child.trigger('emphasis'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + } + if (dataIndex !== null && dataType !== 'edge') { + graph.eachNode(function (node) { + fadeOutItem(node, nodeOpacityPath); + }); + graph.eachEdge(function (edge) { + fadeOutItem(edge, lineOpacityPath); + }); + + var node = graph.getNodeByIndex(dataIndex); + fadeInItem(node, nodeOpacityPath); + zrUtil.each(node.edges, function (edge) { + if (edge.dataIndex < 0) { + return; + } + fadeInItem(edge, lineOpacityPath); + fadeInItem(edge.node1, nodeOpacityPath); + fadeInItem(edge.node2, nodeOpacityPath); + }); + } + }, + + _unfocusAll: function () { + var data = this._model.getData(); + var graph = data.graph; + graph.eachNode(function (node) { + var opacity = getItemOpacity(node, nodeOpacityPath); + node.getGraphicEl().traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + }); + graph.eachEdge(function (edge) { + var opacity = getItemOpacity(edge, lineOpacityPath); + edge.getGraphicEl().traverse(function (child) { + child.trigger('normal'); + if (child.type !== 'group') { + child.setStyle('opacity', opacity); + } + }); + }); + }, + + _startForceLayoutIteration: function (forceLayout, layoutAnimation) { + var self = this; + (function step() { + forceLayout.step(function (stopped) { + self.updateLayout(self._model); + (self._layouting = !stopped) && ( + layoutAnimation + ? (self._layoutTimeout = setTimeout(step, 16)) + : step() + ); + }); + })(); + }, + + _updateController: function (seriesModel, api) { + var controller = this._controller; + var group = this.group; + controller.rectProvider = function () { + var rect = group.getBoundingRect(); + rect.applyTransform(group.transform); + return rect; + }; + if (seriesModel.coordinateSystem.type !== 'view') { + controller.disable(); + return; + } + controller.enable(seriesModel.get('roam')); + controller.zoomLimit = seriesModel.get('scaleLimit'); + // Update zoom from model + controller.zoom = seriesModel.coordinateSystem.getZoom(); + + controller + .off('pan') + .off('zoom') + .on('pan', function (dx, dy) { + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + dx: dx, + dy: dy + }); + }) + .on('zoom', function (zoom, mouseX, mouseY) { + api.dispatchAction({ + seriesId: seriesModel.id, + type: 'graphRoam', + zoom: zoom, + originX: mouseX, + originY: mouseY + }); + this._updateNodeAndLinkScale(); + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + this._lineDraw.updateLayout(); + }, this); + }, + + _updateNodeAndLinkScale: function () { + var seriesModel = this._model; + var data = seriesModel.getData(); + + var nodeScale = this._getNodeGlobalScale(seriesModel); + var invScale = [nodeScale, nodeScale]; + + data.eachItemGraphicEl(function (el, idx) { + el.attr('scale', invScale); + }); + }, + + _getNodeGlobalScale: function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type !== 'view') { + return 1; + } + + var nodeScaleRatio = this._nodeScaleRatio; + + var groupScale = coordSys.scale; + var groupZoom = (groupScale && groupScale[0]) || 1; + // Scale node when zoom changes + var roamZoom = coordSys.getZoom(); + var nodeScale = (roamZoom - 1) * nodeScaleRatio + 1; + + return nodeScale / groupZoom; + }, + + updateLayout: function (seriesModel) { + adjustEdge(seriesModel.getGraph(), this._getNodeGlobalScale(seriesModel)); + + this._symbolDraw.updateLayout(); + this._lineDraw.updateLayout(); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(); + this._lineDraw && this._lineDraw.remove(); + } + }); + + +/***/ }, +/* 199 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/LineDraw + */ + + + var graphic = __webpack_require__(43); + var LineGroup = __webpack_require__(200); + + + function isPointNaN(pt) { + return isNaN(pt[0]) || isNaN(pt[1]); + } + function lineNeedsDraw(pts) { + return !isPointNaN(pts[0]) && !isPointNaN(pts[1]); + } + /** + * @alias module:echarts/component/marker/LineDraw + * @constructor + */ + function LineDraw(ctor) { + this._ctor = ctor || LineGroup; + this.group = new graphic.Group(); + } + + var lineDrawProto = LineDraw.prototype; + + /** + * @param {module:echarts/data/List} lineData + */ + lineDrawProto.updateData = function (lineData) { + + var oldLineData = this._lineData; + var group = this.group; + var LineCtor = this._ctor; + + var hostModel = lineData.hostModel; + + var seriesScope = { + lineStyle: hostModel.getModel('lineStyle.normal').getLineStyle(), + hoverLineStyle: hostModel.getModel('lineStyle.emphasis').getLineStyle(), + labelModel: hostModel.getModel('label.normal'), + hoverLabelModel: hostModel.getModel('label.emphasis') + }; + + lineData.diff(oldLineData) + .add(function (idx) { + if (!lineNeedsDraw(lineData.getItemLayout(idx))) { + return; + } + var lineGroup = new LineCtor(lineData, idx, seriesScope); + + lineData.setItemGraphicEl(idx, lineGroup); + + group.add(lineGroup); + }) + .update(function (newIdx, oldIdx) { + var lineGroup = oldLineData.getItemGraphicEl(oldIdx); + if (!lineNeedsDraw(lineData.getItemLayout(newIdx))) { + group.remove(lineGroup); + return; + } + + if (!lineGroup) { + lineGroup = new LineCtor(lineData, newIdx, seriesScope); + } + else { + lineGroup.updateData(lineData, newIdx, seriesScope); + } + + lineData.setItemGraphicEl(newIdx, lineGroup); + + group.add(lineGroup); + }) + .remove(function (idx) { + group.remove(oldLineData.getItemGraphicEl(idx)); + }) + .execute(); + + this._lineData = lineData; + }; + + lineDrawProto.updateLayout = function () { + var lineData = this._lineData; + lineData.eachItemGraphicEl(function (el, idx) { + el.updateLayout(lineData, idx); + }, this); + }; + + lineDrawProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LineDraw; + + +/***/ }, +/* 200 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Line + */ + + + var symbolUtil = __webpack_require__(106); + var vector = __webpack_require__(10); + // var matrix = require('zrender/lib/core/matrix'); + var LinePath = __webpack_require__(201); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol']; + function makeSymbolTypeKey(symbolCategory) { + return '_' + symbolCategory + 'Type'; + } + /** + * @inner + */ + function createSymbol(name, lineData, idx) { + var color = lineData.getItemVisual(idx, 'color'); + var symbolType = lineData.getItemVisual(idx, name); + var symbolSize = lineData.getItemVisual(idx, name + 'Size'); + + if (!symbolType || symbolType === 'none') { + return; + } + + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [symbolSize, symbolSize]; + } + var symbolPath = symbolUtil.createSymbol( + symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, + symbolSize[0], symbolSize[1], color + ); + symbolPath.name = name; + + return symbolPath; + } + + function createLine(points) { + var line = new LinePath({ + name: 'line' + }); + setLinePoints(line.shape, points); + return line; + } + + function setLinePoints(targetShape, points) { + var p1 = points[0]; + var p2 = points[1]; + var cp1 = points[2]; + targetShape.x1 = p1[0]; + targetShape.y1 = p1[1]; + targetShape.x2 = p2[0]; + targetShape.y2 = p2[1]; + targetShape.percent = 1; + + if (cp1) { + targetShape.cpx1 = cp1[0]; + targetShape.cpy1 = cp1[1]; + } + else { + targetShape.cpx1 = NaN; + targetShape.cpy1 = NaN; + } + } + + function updateSymbolAndLabelBeforeLineUpdate () { + var lineGroup = this; + var symbolFrom = lineGroup.childOfName('fromSymbol'); + var symbolTo = lineGroup.childOfName('toSymbol'); + var label = lineGroup.childOfName('label'); + // Quick reject + if (!symbolFrom && !symbolTo && label.ignore) { + return; + } + + var invScale = 1; + var parentNode = this.parent; + while (parentNode) { + if (parentNode.scale) { + invScale /= parentNode.scale[0]; + } + parentNode = parentNode.parent; + } + + var line = lineGroup.childOfName('line'); + // If line not changed + // FIXME Parent scale changed + if (!this.__dirty && !line.__dirty) { + return; + } + + var percent = line.shape.percent; + var fromPos = line.pointAt(0); + var toPos = line.pointAt(percent); + + var d = vector.sub([], toPos, fromPos); + vector.normalize(d, d); + + if (symbolFrom) { + symbolFrom.attr('position', fromPos); + var tangent = line.tangentAt(0); + symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2( + tangent[1], tangent[0] + )); + symbolFrom.attr('scale', [invScale * percent, invScale * percent]); + } + if (symbolTo) { + symbolTo.attr('position', toPos); + var tangent = line.tangentAt(1); + symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2( + tangent[1], tangent[0] + )); + symbolTo.attr('scale', [invScale * percent, invScale * percent]); + } + + if (!label.ignore) { + label.attr('position', toPos); + + var textPosition; + var textAlign; + var textVerticalAlign; + + var distance = 5 * invScale; + // End + if (label.__position === 'end') { + textPosition = [d[0] * distance + toPos[0], d[1] * distance + toPos[1]]; + textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center'); + textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle'); + } + // Middle + else if (label.__position === 'middle') { + var halfPercent = percent / 2; + var tangent = line.tangentAt(halfPercent); + var n = [tangent[1], -tangent[0]]; + var cp = line.pointAt(halfPercent); + if (n[1] > 0) { + n[0] = -n[0]; + n[1] = -n[1]; + } + textPosition = [cp[0] + n[0] * distance, cp[1] + n[1] * distance]; + textAlign = 'center'; + textVerticalAlign = 'bottom'; + var rotation = -Math.atan2(tangent[1], tangent[0]); + if (toPos[0] < fromPos[0]) { + rotation = Math.PI + rotation; + } + label.attr('rotation', rotation); + } + // Start + else { + textPosition = [-d[0] * distance + fromPos[0], -d[1] * distance + fromPos[1]]; + textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center'); + textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle'); + } + label.attr({ + style: { + // Use the user specified text align and baseline first + textVerticalAlign: label.__verticalAlign || textVerticalAlign, + textAlign: label.__textAlign || textAlign + }, + position: textPosition, + scale: [invScale, invScale] + }); + } + } + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Line} + */ + function Line(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this._createLine(lineData, idx, seriesScope); + } + + var lineProto = Line.prototype; + + // Update symbol position and rotation + lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate; + + lineProto._createLine = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + var linePoints = lineData.getItemLayout(idx); + + var line = createLine(linePoints); + line.shape.percent = 0; + graphic.initProps(line, { + shape: { + percent: 1 + } + }, seriesModel, idx); + + this.add(line); + + var label = new graphic.Text({ + name: 'label' + }); + this.add(label); + + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = createSymbol(symbolCategory, lineData, idx); + // symbols must added after line to make sure + // it will be updated after line#update. + // Or symbol position and rotation update in line#beforeUpdate will be one frame slow + this.add(symbol); + this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory); + }, this); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + lineProto.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childOfName('line'); + var linePoints = lineData.getItemLayout(idx); + var target = { + shape: {} + }; + setLinePoints(target.shape, linePoints); + graphic.updateProps(line, target, seriesModel, idx); + + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbolType = lineData.getItemVisual(idx, symbolCategory); + var key = makeSymbolTypeKey(symbolCategory); + // Symbol changed + if (this[key] !== symbolType) { + this.remove(this.childOfName(symbolCategory)); + var symbol = createSymbol(symbolCategory, lineData, idx); + this.add(symbol); + } + this[key] = symbolType; + }, this); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + lineProto._updateCommonStl = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childOfName('line'); + + var lineStyle = seriesScope && seriesScope.lineStyle; + var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; + var labelModel = seriesScope && seriesScope.labelModel; + var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; + + // Optimization for large dataset + if (!seriesScope || lineData.hasItemOption) { + var itemModel = lineData.getItemModel(idx); + + lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle(); + hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + + labelModel = itemModel.getModel('label.normal'); + hoverLabelModel = itemModel.getModel('label.emphasis'); + } + + var visualColor = lineData.getItemVisual(idx, 'color'); + var visualOpacity = zrUtil.retrieve( + lineData.getItemVisual(idx, 'opacity'), + lineStyle.opacity, + 1 + ); + if (isNaN(defaultText)) { + // Use name + defaultText = lineData.getName(idx); + } + line.useStyle(zrUtil.defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor, + opacity: visualOpacity + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + // Update symbol + zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) { + var symbol = this.childOfName(symbolCategory); + if (symbol) { + symbol.setColor(visualColor); + symbol.setStyle({ + opacity: visualOpacity + }); + } + }, this); + + var showLabel = labelModel.getShallow('show'); + var hoverShowLabel = hoverLabelModel.getShallow('show'); + var defaultText; + var label = this.childOfName('label'); + var defaultLabelColor; + if (showLabel || hoverShowLabel) { + defaultText = numberUtil.round(seriesModel.getRawValue(idx)); + defaultLabelColor = visualColor || '#000'; + } + // label.afterUpdate = lineAfterUpdate; + if (showLabel) { + var textStyleModel = labelModel.getModel('textStyle'); + label.setStyle({ + text: zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType), + defaultText + ), + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() || defaultLabelColor + }); + + label.__textAlign = textStyleModel.get('align'); + label.__verticalAlign = textStyleModel.get('baseline'); + label.__position = labelModel.get('position'); + } + else { + label.setStyle('text', ''); + } + if (hoverShowLabel) { + var textStyleHoverModel = hoverLabelModel.getModel('textStyle'); + + label.hoverStyle = { + text: zrUtil.retrieve( + seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), + defaultText + ), + textFont: textStyleHoverModel.getFont(), + fill: textStyleHoverModel.getTextColor() || defaultLabelColor + }; + } + else { + label.hoverStyle = { + text: '' + }; + } + + label.ignore = !showLabel && !hoverShowLabel; + + graphic.setHoverStyle(this); + }; + + lineProto.updateLayout = function (lineData, idx) { + this.setLinePoints(lineData.getItemLayout(idx)); + }; + + lineProto.setLinePoints = function (points) { + var linePath = this.childOfName('line'); + setLinePoints(linePath.shape, points); + linePath.dirty(); + }; + + zrUtil.inherits(Line, graphic.Group); + + module.exports = Line; + + +/***/ }, +/* 201 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Line path for bezier and straight line draw + */ + + var graphic = __webpack_require__(43); + var vec2 = __webpack_require__(10); + + var straightLineProto = graphic.Line.prototype; + var bezierCurveProto = graphic.BezierCurve.prototype; + + function isLine(shape) { + return isNaN(+shape.cpx1) || isNaN(+shape.cpy1); + } + + module.exports = graphic.extendShape({ + + type: 'ec-line', + + style: { + stroke: '#000', + fill: null + }, + + shape: { + x1: 0, + y1: 0, + x2: 0, + y2: 0, + percent: 1, + cpx1: null, + cpy1: null + }, + + buildPath: function (ctx, shape) { + (isLine(shape) ? straightLineProto : bezierCurveProto).buildPath(ctx, shape); + }, + + pointAt: function (t) { + return isLine(this.shape) + ? straightLineProto.pointAt.call(this, t) + : bezierCurveProto.pointAt.call(this, t); + }, + + tangentAt: function (t) { + var shape = this.shape; + var p = isLine(shape) + ? [shape.x2 - shape.x1, shape.y2 - shape.y1] + : bezierCurveProto.tangentAt.call(this, t); + return vec2.normalize(p, p); + } + }); + + +/***/ }, +/* 202 */ +/***/ function(module, exports, __webpack_require__) { + + + + var curveTool = __webpack_require__(50); + var vec2 = __webpack_require__(10); + + var v1 = []; + var v2 = []; + var v3 = []; + var quadraticAt = curveTool.quadraticAt; + var v2DistSquare = vec2.distSquare; + var mathAbs = Math.abs; + function intersectCurveCircle(curvePoints, center, radius) { + var p0 = curvePoints[0]; + var p1 = curvePoints[1]; + var p2 = curvePoints[2]; + + var d = Infinity; + var t; + var radiusSquare = radius * radius; + var interval = 0.1; + + for (var _t = 0.1; _t <= 0.9; _t += 0.1) { + v1[0] = quadraticAt(p0[0], p1[0], p2[0], _t); + v1[1] = quadraticAt(p0[1], p1[1], p2[1], _t); + var diff = mathAbs(v2DistSquare(v1, center) - radiusSquare); + if (diff < d) { + d = diff; + t = _t; + } + } + + // Assume the segment is monotone,Find root through Bisection method + // At most 32 iteration + for (var i = 0; i < 32; i++) { + // var prev = t - interval; + var next = t + interval; + // v1[0] = quadraticAt(p0[0], p1[0], p2[0], prev); + // v1[1] = quadraticAt(p0[1], p1[1], p2[1], prev); + v2[0] = quadraticAt(p0[0], p1[0], p2[0], t); + v2[1] = quadraticAt(p0[1], p1[1], p2[1], t); + v3[0] = quadraticAt(p0[0], p1[0], p2[0], next); + v3[1] = quadraticAt(p0[1], p1[1], p2[1], next); + + var diff = v2DistSquare(v2, center) - radiusSquare; + if (mathAbs(diff) < 1e-2) { + break; + } + + // var prevDiff = v2DistSquare(v1, center) - radiusSquare; + var nextDiff = v2DistSquare(v3, center) - radiusSquare; + + interval /= 2; + if (diff < 0) { + if (nextDiff >= 0) { + t = t + interval; + } + else { + t = t - interval; + } + } + else { + if (nextDiff >= 0) { + t = t - interval; + } + else { + t = t + interval; + } + } + } + + return t; + } + // Adjust edge to avoid + module.exports = function (graph, scale) { + var tmp0 = []; + var quadraticSubdivide = curveTool.quadraticSubdivide; + var pts = [[], [], []]; + var pts2 = [[], []]; + var v = []; + scale /= 2; + + function getSymbolSize(node) { + var symbolSize = node.getVisual('symbolSize'); + if (symbolSize instanceof Array) { + symbolSize = (symbolSize[0] + symbolSize[1]) / 2; + } + return symbolSize; + } + graph.eachEdge(function (edge, idx) { + var linePoints = edge.getLayout(); + var fromSymbol = edge.getVisual('fromSymbol'); + var toSymbol = edge.getVisual('toSymbol'); + + if (!linePoints.__original) { + linePoints.__original = [ + vec2.clone(linePoints[0]), + vec2.clone(linePoints[1]) + ]; + if (linePoints[2]) { + linePoints.__original.push(vec2.clone(linePoints[2])); + } + } + var originalPoints = linePoints.__original; + // Quadratic curve + if (linePoints[2] != null) { + vec2.copy(pts[0], originalPoints[0]); + vec2.copy(pts[1], originalPoints[2]); + vec2.copy(pts[2], originalPoints[1]); + if (fromSymbol && fromSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node1); + + var t = intersectCurveCircle(pts, originalPoints[0], symbolSize * scale); + // Subdivide and get the second + quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[0][0] = tmp0[3]; + pts[1][0] = tmp0[4]; + quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[0][1] = tmp0[3]; + pts[1][1] = tmp0[4]; + } + if (toSymbol && toSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node2); + + var t = intersectCurveCircle(pts, originalPoints[1], symbolSize * scale); + // Subdivide and get the first + quadraticSubdivide(pts[0][0], pts[1][0], pts[2][0], t, tmp0); + pts[1][0] = tmp0[1]; + pts[2][0] = tmp0[2]; + quadraticSubdivide(pts[0][1], pts[1][1], pts[2][1], t, tmp0); + pts[1][1] = tmp0[1]; + pts[2][1] = tmp0[2]; + } + // Copy back to layout + vec2.copy(linePoints[0], pts[0]); + vec2.copy(linePoints[1], pts[2]); + vec2.copy(linePoints[2], pts[1]); + } + // Line + else { + vec2.copy(pts2[0], originalPoints[0]); + vec2.copy(pts2[1], originalPoints[1]); + + vec2.sub(v, pts2[1], pts2[0]); + vec2.normalize(v, v); + if (fromSymbol && fromSymbol != 'none') { + + var symbolSize = getSymbolSize(edge.node1); + + vec2.scaleAndAdd(pts2[0], pts2[0], v, symbolSize * scale); + } + if (toSymbol && toSymbol != 'none') { + var symbolSize = getSymbolSize(edge.node2); + + vec2.scaleAndAdd(pts2[1], pts2[1], v, -symbolSize * scale); + } + vec2.copy(linePoints[0], pts2[0]); + vec2.copy(linePoints[1], pts2[1]); + } + }); + }; + + +/***/ }, +/* 203 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + var roamHelper = __webpack_require__(177); + + var actionInfo = { + type: 'graphRoam', + event: 'graphRoam', + update: 'none' + }; + + /** + * @payload + * @property {string} name Series name + * @property {number} [dx] + * @property {number} [dy] + * @property {number} [zoom] + * @property {number} [originX] + * @property {number} [originY] + */ + + echarts.registerAction(actionInfo, function (payload, ecModel) { + ecModel.eachComponent({mainType: 'series', query: payload}, function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + + var res = roamHelper.updateCenterAndZoom(coordSys, payload); + + seriesModel.setCenter + && seriesModel.setCenter(res.center); + + seriesModel.setZoom + && seriesModel.setZoom(res.zoom); + }); + }); + + +/***/ }, +/* 204 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (!legendModels || !legendModels.length) { + return; + } + ecModel.eachSeriesByType('graph', function (graphSeries) { + var categoriesData = graphSeries.getCategoriesData(); + var graph = graphSeries.getGraph(); + var data = graph.data; + + var categoryNames = categoriesData.mapArray(categoriesData.getName); + + data.filterSelf(function (idx) { + var model = data.getItemModel(idx); + var category = model.getShallow('category'); + if (category != null) { + if (typeof category === 'number') { + category = categoryNames[category]; + } + // If in any legend component the status is not selected. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(category)) { + return false; + } + } + } + return true; + }); + }, this); + }; + + +/***/ }, +/* 205 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + + var paletteScope = {}; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var categoriesData = seriesModel.getCategoriesData(); + var data = seriesModel.getData(); + + var categoryNameIdxMap = {}; + + categoriesData.each(function (idx) { + var name = categoriesData.getName(idx); + categoryNameIdxMap[name] = idx; + + var itemModel = categoriesData.getItemModel(idx); + var color = itemModel.get('itemStyle.normal.color') + || seriesModel.getColorFromPalette(name, paletteScope); + categoriesData.setItemVisual(idx, 'color', color); + }); + + // Assign category color to visual + if (categoriesData.count()) { + data.each(function (idx) { + var model = data.getItemModel(idx); + var category = model.getShallow('category'); + if (category != null) { + if (typeof category === 'string') { + category = categoryNameIdxMap[category]; + } + if (!data.getItemVisual(idx, 'color', true)) { + data.setItemVisual( + idx, 'color', + categoriesData.getItemVisual(category, 'color') + ); + } + } + }); + } + }); + }; + + +/***/ }, +/* 206 */ +/***/ function(module, exports) { + + + + function normalize(a) { + if (!(a instanceof Array)) { + a = [a, a]; + } + return a; + } + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var graph = seriesModel.getGraph(); + var edgeData = seriesModel.getEdgeData(); + var symbolType = normalize(seriesModel.get('edgeSymbol')); + var symbolSize = normalize(seriesModel.get('edgeSymbolSize')); + + var colorQuery = 'lineStyle.normal.color'.split('.'); + var opacityQuery = 'lineStyle.normal.opacity'.split('.'); + + edgeData.setVisual('fromSymbol', symbolType && symbolType[0]); + edgeData.setVisual('toSymbol', symbolType && symbolType[1]); + edgeData.setVisual('fromSymbolSize', symbolSize && symbolSize[0]); + edgeData.setVisual('toSymbolSize', symbolSize && symbolSize[1]); + edgeData.setVisual('color', seriesModel.get(colorQuery)); + edgeData.setVisual('opacity', seriesModel.get(opacityQuery)); + + edgeData.each(function (idx) { + var itemModel = edgeData.getItemModel(idx); + var edge = graph.getEdgeByIndex(idx); + var symbolType = normalize(itemModel.getShallow('symbol', true)); + var symbolSize = normalize(itemModel.getShallow('symbolSize', true)); + // Edge visual must after node visual + var color = itemModel.get(colorQuery); + var opacity = itemModel.get(opacityQuery); + switch (color) { + case 'source': + color = edge.node1.getVisual('color'); + break; + case 'target': + color = edge.node2.getVisual('color'); + break; + } + + symbolType[0] && edge.setVisual('fromSymbol', symbolType[0]); + symbolType[1] && edge.setVisual('toSymbol', symbolType[1]); + symbolSize[0] && edge.setVisual('fromSymbolSize', symbolSize[0]); + symbolSize[1] && edge.setVisual('toSymbolSize', symbolSize[1]); + + edge.setVisual('color', color); + edge.setVisual('opacity', opacity); + }); + }); + }; + + +/***/ }, +/* 207 */ +/***/ function(module, exports, __webpack_require__) { + + + + var simpleLayoutHelper = __webpack_require__(208); + var simpleLayoutEdge = __webpack_require__(209); + module.exports = function (ecModel, api) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + var layout = seriesModel.get('layout'); + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + var data = seriesModel.getData(); + data.each(coordSys.dimensions, function (x, y, idx) { + if (!isNaN(x) && !isNaN(y)) { + data.setItemLayout(idx, coordSys.dataToPoint([x, y])); + } + else { + // Also {Array.}, not undefined to avoid if...else... statement + data.setItemLayout(idx, [NaN, NaN]); + } + }); + + simpleLayoutEdge(data.graph); + } + else if (!layout || layout === 'none') { + simpleLayoutHelper(seriesModel); + } + }); + }; + + +/***/ }, +/* 208 */ +/***/ function(module, exports, __webpack_require__) { + + + + var simpleLayoutEdge = __webpack_require__(209); + + module.exports = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + var graph = seriesModel.getGraph(); + + graph.eachNode(function (node) { + var model = node.getModel(); + node.setLayout([+model.get('x'), +model.get('y')]); + }); + + simpleLayoutEdge(graph); + }; + + +/***/ }, +/* 209 */ +/***/ function(module, exports, __webpack_require__) { + + + var vec2 = __webpack_require__(10); + module.exports = function (graph) { + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0; + var p1 = vec2.clone(edge.node1.getLayout()); + var p2 = vec2.clone(edge.node2.getLayout()); + var points = [p1, p2]; + if (+curveness) { + points.push([ + (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * curveness, + (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * curveness + ]); + } + edge.setLayout(points); + }); + }; + + +/***/ }, +/* 210 */ +/***/ function(module, exports, __webpack_require__) { + + + var circularLayoutHelper = __webpack_require__(211); + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (seriesModel) { + if (seriesModel.get('layout') === 'circular') { + circularLayoutHelper(seriesModel); + } + }); + }; + + +/***/ }, +/* 211 */ +/***/ function(module, exports, __webpack_require__) { + + + var vec2 = __webpack_require__(10); + module.exports = function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + + var rect = coordSys.getBoundingRect(); + + var nodeData = seriesModel.getData(); + var graph = nodeData.graph; + + var angle = 0; + var sum = nodeData.getSum('value'); + var unitAngle = Math.PI * 2 / (sum || nodeData.count()); + + var cx = rect.width / 2 + rect.x; + var cy = rect.height / 2 + rect.y; + + var r = Math.min(rect.width, rect.height) / 2; + + graph.eachNode(function (node) { + var value = node.getValue('value'); + + angle += unitAngle * (sum ? value : 2) / 2; + + node.setLayout([ + r * Math.cos(angle) + cx, + r * Math.sin(angle) + cy + ]); + + angle += unitAngle * (sum ? value : 2) / 2; + }); + + nodeData.setLayout({ + cx: cx, + cy: cy + }); + + graph.eachEdge(function (edge) { + var curveness = edge.getModel().get('lineStyle.normal.curveness') || 0; + var p1 = vec2.clone(edge.node1.getLayout()); + var p2 = vec2.clone(edge.node2.getLayout()); + var cp1; + var x12 = (p1[0] + p2[0]) / 2; + var y12 = (p1[1] + p2[1]) / 2; + if (+curveness) { + curveness *= 3; + cp1 = [ + cx * curveness + x12 * (1 - curveness), + cy * curveness + y12 * (1 - curveness) + ]; + } + edge.setLayout([p1, p2, cp1]); + }); + }; + + +/***/ }, +/* 212 */ +/***/ function(module, exports, __webpack_require__) { + + + + var forceHelper = __webpack_require__(213); + var numberUtil = __webpack_require__(7); + var simpleLayoutHelper = __webpack_require__(208); + var circularLayoutHelper = __webpack_require__(211); + var vec2 = __webpack_require__(10); + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('graph', function (graphSeries) { + var coordSys = graphSeries.coordinateSystem; + if (coordSys && coordSys.type !== 'view') { + return; + } + if (graphSeries.get('layout') === 'force') { + var preservedPoints = graphSeries.preservedPoints || {}; + var graph = graphSeries.getGraph(); + var nodeData = graph.data; + var edgeData = graph.edgeData; + var forceModel = graphSeries.getModel('force'); + var initLayout = forceModel.get('initLayout'); + if (graphSeries.preservedPoints) { + nodeData.each(function (idx) { + var id = nodeData.getId(idx); + nodeData.setItemLayout(idx, preservedPoints[id] || [NaN, NaN]); + }); + } + else if (!initLayout || initLayout === 'none') { + simpleLayoutHelper(graphSeries); + } + else if (initLayout === 'circular') { + circularLayoutHelper(graphSeries); + } + + var nodeDataExtent = nodeData.getDataExtent('value'); + var edgeDataExtent = edgeData.getDataExtent('value'); + // var edgeDataExtent = edgeData.getDataExtent('value'); + var repulsion = forceModel.get('repulsion'); + var edgeLength = forceModel.get('edgeLength'); + if (!zrUtil.isArray(repulsion)) { + repulsion = [repulsion, repulsion]; + } + if (!zrUtil.isArray(edgeLength)) { + edgeLength = [edgeLength, edgeLength]; + } + // Larger value has smaller length + edgeLength = [edgeLength[1], edgeLength[0]]; + + var nodes = nodeData.mapArray('value', function (value, idx) { + var point = nodeData.getItemLayout(idx); + // var w = numberUtil.linearMap(value, nodeDataExtent, [0, 50]); + var rep = numberUtil.linearMap(value, nodeDataExtent, repulsion); + if (isNaN(rep)) { + rep = (repulsion[0] + repulsion[1]) / 2; + } + return { + w: rep, + rep: rep, + p: (!point || isNaN(point[0]) || isNaN(point[1])) ? null : point + }; + }); + var edges = edgeData.mapArray('value', function (value, idx) { + var edge = graph.getEdgeByIndex(idx); + var d = numberUtil.linearMap(value, edgeDataExtent, edgeLength); + if (isNaN(d)) { + d = (edgeLength[0] + edgeLength[1]) / 2; + } + return { + n1: nodes[edge.node1.dataIndex], + n2: nodes[edge.node2.dataIndex], + d: d, + curveness: edge.getModel().get('lineStyle.normal.curveness') || 0 + }; + }); + + var coordSys = graphSeries.coordinateSystem; + var rect = coordSys.getBoundingRect(); + var forceInstance = forceHelper(nodes, edges, { + rect: rect, + gravity: forceModel.get('gravity') + }); + var oldStep = forceInstance.step; + forceInstance.step = function (cb) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (nodes[i].fixed) { + // Write back to layout instance + vec2.copy(nodes[i].p, graph.getNodeByIndex(i).getLayout()); + } + } + oldStep(function (nodes, edges, stopped) { + for (var i = 0, l = nodes.length; i < l; i++) { + if (!nodes[i].fixed) { + graph.getNodeByIndex(i).setLayout(nodes[i].p); + } + preservedPoints[nodeData.getId(i)] = nodes[i].p; + } + for (var i = 0, l = edges.length; i < l; i++) { + var e = edges[i]; + var edge = graph.getEdgeByIndex(i); + var p1 = e.n1.p; + var p2 = e.n2.p; + var points = edge.getLayout(); + points = points ? points.slice() : []; + points[0] = points[0] || []; + points[1] = points[1] || []; + vec2.copy(points[0], p1); + vec2.copy(points[1], p2); + if (+e.curveness) { + points[2] = [ + (p1[0] + p2[0]) / 2 - (p1[1] - p2[1]) * e.curveness, + (p1[1] + p2[1]) / 2 - (p2[0] - p1[0]) * e.curveness + ]; + } + edge.setLayout(points); + } + // Update layout + + cb && cb(stopped); + }); + }; + graphSeries.forceLayout = forceInstance; + graphSeries.preservedPoints = preservedPoints; + + // Step to get the layout + forceInstance.step(); + } + else { + // Remove prev injected forceLayout instance + graphSeries.forceLayout = null; + } + }); + }; + + +/***/ }, +/* 213 */ +/***/ function(module, exports, __webpack_require__) { + + + + var vec2 = __webpack_require__(10); + var scaleAndAdd = vec2.scaleAndAdd; + + // function adjacentNode(n, e) { + // return e.n1 === n ? e.n2 : e.n1; + // } + + module.exports = function (nodes, edges, opts) { + var rect = opts.rect; + var width = rect.width; + var height = rect.height; + var center = [rect.x + width / 2, rect.y + height / 2]; + // var scale = opts.scale || 1; + var gravity = opts.gravity == null ? 0.1 : opts.gravity; + + // for (var i = 0; i < edges.length; i++) { + // var e = edges[i]; + // var n1 = e.n1; + // var n2 = e.n2; + // n1.edges = n1.edges || []; + // n2.edges = n2.edges || []; + // n1.edges.push(e); + // n2.edges.push(e); + // } + // Init position + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + if (!n.p) { + // Use the position from first adjecent node with defined position + // Or use a random position + // From d3 + // if (n.edges) { + // var j = -1; + // while (++j < n.edges.length) { + // var e = n.edges[j]; + // var other = adjacentNode(n, e); + // if (other.p) { + // n.p = vec2.clone(other.p); + // break; + // } + // } + // } + // if (!n.p) { + n.p = vec2.create( + width * (Math.random() - 0.5) + center[0], + height * (Math.random() - 0.5) + center[1] + ); + // } + } + n.pp = vec2.clone(n.p); + n.edges = null; + } + + // Formula in 'Graph Drawing by Force-directed Placement' + // var k = scale * Math.sqrt(width * height / nodes.length); + // var k2 = k * k; + + var friction = 0.6; + + return { + warmUp: function () { + friction = 0.5; + }, + + setFixed: function (idx) { + nodes[idx].fixed = true; + }, + + setUnfixed: function (idx) { + nodes[idx].fixed = false; + }, + + step: function (cb) { + var v12 = []; + var nLen = nodes.length; + for (var i = 0; i < edges.length; i++) { + var e = edges[i]; + var n1 = e.n1; + var n2 = e.n2; + + vec2.sub(v12, n2.p, n1.p); + var d = vec2.len(v12) - e.d; + var w = n2.w / (n1.w + n2.w); + vec2.normalize(v12, v12); + + !n1.fixed && scaleAndAdd(n1.p, n1.p, v12, w * d * friction); + !n2.fixed && scaleAndAdd(n2.p, n2.p, v12, -(1 - w) * d * friction); + } + // Gravity + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + vec2.sub(v12, center, n.p); + // var d = vec2.len(v12); + // vec2.scale(v12, v12, 1 / d); + // var gravityFactor = gravity; + vec2.scaleAndAdd(n.p, n.p, v12, gravity * friction); + } + } + + // Repulsive + // PENDING + for (var i = 0; i < nLen; i++) { + var n1 = nodes[i]; + for (var j = i + 1; j < nLen; j++) { + var n2 = nodes[j]; + vec2.sub(v12, n2.p, n1.p); + var d = vec2.len(v12); + if (d === 0) { + // Random repulse + vec2.set(v12, Math.random() - 0.5, Math.random() - 0.5); + d = 1; + } + var repFact = (n1.rep + n2.rep) / d / d; + !n1.fixed && scaleAndAdd(n1.pp, n1.pp, v12, repFact); + !n2.fixed && scaleAndAdd(n2.pp, n2.pp, v12, -repFact); + } + } + var v = []; + for (var i = 0; i < nLen; i++) { + var n = nodes[i]; + if (!n.fixed) { + vec2.sub(v, n.p, n.pp); + vec2.scaleAndAdd(n.p, n.p, v, friction); + vec2.copy(n.pp, n.p); + } + } + + friction = friction * 0.992; + + cb && cb(nodes, edges, friction < 0.01); + } + }; + }; + + +/***/ }, +/* 214 */ +/***/ function(module, exports, __webpack_require__) { + + + // FIXME Where to create the simple view coordinate system + var View = __webpack_require__(168); + var layout = __webpack_require__(21); + var bbox = __webpack_require__(51); + + function getViewRect(seriesModel, api, aspect) { + var option = seriesModel.getBoxLayoutParams(); + option.aspect = aspect; + return layout.getLayoutRect(option, { + width: api.getWidth(), + height: api.getHeight() + }); + } + + module.exports = function (ecModel, api) { + var viewList = []; + ecModel.eachSeriesByType('graph', function (seriesModel) { + var coordSysType = seriesModel.get('coordinateSystem'); + if (!coordSysType || coordSysType === 'view') { + var viewCoordSys = new View(); + viewList.push(viewCoordSys); + + var data = seriesModel.getData(); + var positions = data.mapArray(function (idx) { + var itemModel = data.getItemModel(idx); + return [+itemModel.get('x'), +itemModel.get('y')]; + }); + + var min = []; + var max = []; + + bbox.fromPoints(positions, min, max); + + // If width or height is 0 + if (max[0] - min[0] === 0) { + max[0] += 1; + min[0] -= 1; + } + if (max[1] - min[1] === 0) { + max[1] += 1; + min[1] -= 1; + } + var aspect = (max[0] - min[0]) / (max[1] - min[1]); + // FIXME If get view rect after data processed? + var viewRect = getViewRect(seriesModel, api, aspect); + // Position may be NaN, use view rect instead + if (isNaN(aspect)) { + min = [viewRect.x, viewRect.y]; + max = [viewRect.x + viewRect.width, viewRect.y + viewRect.height]; + } + + var bbWidth = max[0] - min[0]; + var bbHeight = max[1] - min[1]; + + var viewWidth = viewRect.width; + var viewHeight = viewRect.height; + + viewCoordSys = seriesModel.coordinateSystem = new View(); + viewCoordSys.zoomLimit = seriesModel.get('scaleLimit'); + + viewCoordSys.setBoundingRect( + min[0], min[1], bbWidth, bbHeight + ); + viewCoordSys.setViewRect( + viewRect.x, viewRect.y, viewWidth, viewHeight + ); + + // Update roam info + viewCoordSys.setCenter(seriesModel.get('center')); + viewCoordSys.setZoom(seriesModel.get('zoom')); + } + }); + return viewList; + }; + + +/***/ }, +/* 215 */ +/***/ function(module, exports, __webpack_require__) { + + + __webpack_require__(216); + __webpack_require__(217); + + +/***/ }, +/* 216 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var SeriesModel = __webpack_require__(28); + var zrUtil = __webpack_require__(4); + + var GaugeSeries = SeriesModel.extend({ + + type: 'series.gauge', + + getInitialData: function (option, ecModel) { + var list = new List(['value'], this); + var dataOpt = option.data || []; + if (!zrUtil.isArray(dataOpt)) { + dataOpt = [dataOpt]; + } + // Only use the first data item + list.initData(dataOpt); + return list; + }, + + defaultOption: { + zlevel: 0, + z: 2, + // 默认全局居中 + center: ['50%', '50%'], + legendHoverLink: true, + radius: '75%', + startAngle: 225, + endAngle: -45, + clockwise: true, + // 最小值 + min: 0, + // 最大值 + max: 100, + // 分割段数,默认为10 + splitNumber: 10, + // 坐标轴线 + axisLine: { + // 默认显示,属性show控制显示与否 + show: true, + lineStyle: { // 属性lineStyle控制线条样式 + color: [[0.2, '#91c7ae'], [0.8, '#63869e'], [1, '#c23531']], + width: 30 + } + }, + // 分隔线 + splitLine: { + // 默认显示,属性show控制显示与否 + show: true, + // 属性length控制线长 + length: 30, + // 属性lineStyle(详见lineStyle)控制线条样式 + lineStyle: { + color: '#eee', + width: 2, + type: 'solid' + } + }, + // 坐标轴小标记 + axisTick: { + // 属性show控制显示与否,默认不显示 + show: true, + // 每份split细分多少段 + splitNumber: 5, + // 属性length控制线长 + length: 8, + // 属性lineStyle控制线条样式 + lineStyle: { + color: '#eee', + width: 1, + type: 'solid' + } + }, + axisLabel: { + show: true, + distance: 5, + // formatter: null, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: 'auto' + } + }, + pointer: { + show: true, + length: '80%', + width: 8 + }, + itemStyle: { + normal: { + color: 'auto' + } + }, + title: { + show: true, + // x, y,单位px + offsetCenter: [0, '-40%'], + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + color: '#333', + fontSize: 15 + } + }, + detail: { + show: true, + backgroundColor: 'rgba(0,0,0,0)', + borderWidth: 0, + borderColor: '#ccc', + width: 100, + height: 40, + // x, y,单位px + offsetCenter: [0, '40%'], + // formatter: null, + // 其余属性默认使用全局文本样式,详见TEXTSTYLE + textStyle: { + color: 'auto', + fontSize: 30 + } + } + } + }); + + module.exports = GaugeSeries; + + +/***/ }, +/* 217 */ +/***/ function(module, exports, __webpack_require__) { + + + + var PointerPath = __webpack_require__(218); + + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + + function parsePosition(seriesModel, api) { + var center = seriesModel.get('center'); + var width = api.getWidth(); + var height = api.getHeight(); + var size = Math.min(width, height); + var cx = parsePercent(center[0], api.getWidth()); + var cy = parsePercent(center[1], api.getHeight()); + var r = parsePercent(seriesModel.get('radius'), size / 2); + + return { + cx: cx, + cy: cy, + r: r + }; + } + + function formatLabel(label, labelFormatter) { + if (labelFormatter) { + if (typeof labelFormatter === 'string') { + label = labelFormatter.replace('{value}', label); + } + else if (typeof labelFormatter === 'function') { + label = labelFormatter(label); + } + } + + return label; + } + + var PI2 = Math.PI * 2; + + var GaugeView = __webpack_require__(42).extend({ + + type: 'gauge', + + render: function (seriesModel, ecModel, api) { + + this.group.removeAll(); + + var colorList = seriesModel.get('axisLine.lineStyle.color'); + var posInfo = parsePosition(seriesModel, api); + + this._renderMain( + seriesModel, ecModel, api, colorList, posInfo + ); + }, + + _renderMain: function (seriesModel, ecModel, api, colorList, posInfo) { + var group = this.group; + + var axisLineModel = seriesModel.getModel('axisLine'); + var lineStyleModel = axisLineModel.getModel('lineStyle'); + + var clockwise = seriesModel.get('clockwise'); + var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI; + var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI; + + var angleRangeSpan = (endAngle - startAngle) % PI2; + + var prevEndAngle = startAngle; + var axisLineWidth = lineStyleModel.get('width'); + + for (var i = 0; i < colorList.length; i++) { + // Clamp + var percent = Math.min(Math.max(colorList[i][0], 0), 1); + var endAngle = startAngle + angleRangeSpan * percent; + var sector = new graphic.Sector({ + shape: { + startAngle: prevEndAngle, + endAngle: endAngle, + cx: posInfo.cx, + cy: posInfo.cy, + clockwise: clockwise, + r0: posInfo.r - axisLineWidth, + r: posInfo.r + }, + silent: true + }); + + sector.setStyle({ + fill: colorList[i][1] + }); + + sector.setStyle(lineStyleModel.getLineStyle( + // Because we use sector to simulate arc + // so the properties for stroking are useless + ['color', 'borderWidth', 'borderColor'] + )); + + group.add(sector); + + prevEndAngle = endAngle; + } + + var getColor = function (percent) { + // Less than 0 + if (percent <= 0) { + return colorList[0][1]; + } + for (var i = 0; i < colorList.length; i++) { + if (colorList[i][0] >= percent + && (i === 0 ? 0 : colorList[i - 1][0]) < percent + ) { + return colorList[i][1]; + } + } + // More than 1 + return colorList[i - 1][1]; + }; + + if (!clockwise) { + var tmp = startAngle; + startAngle = endAngle; + endAngle = tmp; + } + + this._renderTicks( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ); + + this._renderPointer( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ); + + this._renderTitle( + seriesModel, ecModel, api, getColor, posInfo + ); + this._renderDetail( + seriesModel, ecModel, api, getColor, posInfo + ); + }, + + _renderTicks: function ( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ) { + var group = this.group; + var cx = posInfo.cx; + var cy = posInfo.cy; + var r = posInfo.r; + + var minVal = seriesModel.get('min'); + var maxVal = seriesModel.get('max'); + + var splitLineModel = seriesModel.getModel('splitLine'); + var tickModel = seriesModel.getModel('axisTick'); + var labelModel = seriesModel.getModel('axisLabel'); + + var splitNumber = seriesModel.get('splitNumber'); + var subSplitNumber = tickModel.get('splitNumber'); + + var splitLineLen = parsePercent( + splitLineModel.get('length'), r + ); + var tickLen = parsePercent( + tickModel.get('length'), r + ); + + var angle = startAngle; + var step = (endAngle - startAngle) / splitNumber; + var subStep = step / subSplitNumber; + + var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle(); + var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle(); + var textStyleModel = labelModel.getModel('textStyle'); + + for (var i = 0; i <= splitNumber; i++) { + var unitX = Math.cos(angle); + var unitY = Math.sin(angle); + // Split line + if (splitLineModel.get('show')) { + var splitLine = new graphic.Line({ + shape: { + x1: unitX * r + cx, + y1: unitY * r + cy, + x2: unitX * (r - splitLineLen) + cx, + y2: unitY * (r - splitLineLen) + cy + }, + style: splitLineStyle, + silent: true + }); + if (splitLineStyle.stroke === 'auto') { + splitLine.setStyle({ + stroke: getColor(i / splitNumber) + }); + } + + group.add(splitLine); + } + + // Label + if (labelModel.get('show')) { + var label = formatLabel( + numberUtil.round(i / splitNumber * (maxVal - minVal) + minVal), + labelModel.get('formatter') + ); + var distance = labelModel.get('distance'); + + var text = new graphic.Text({ + style: { + text: label, + x: unitX * (r - splitLineLen - distance) + cx, + y: unitY * (r - splitLineLen - distance) + cy, + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textVerticalAlign: unitY < -0.4 ? 'top' : (unitY > 0.4 ? 'bottom' : 'middle'), + textAlign: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center') + }, + silent: true + }); + if (text.style.fill === 'auto') { + text.setStyle({ + fill: getColor(i / splitNumber) + }); + } + + group.add(text); + } + + // Axis tick + if (tickModel.get('show') && i !== splitNumber) { + for (var j = 0; j <= subSplitNumber; j++) { + var unitX = Math.cos(angle); + var unitY = Math.sin(angle); + var tickLine = new graphic.Line({ + shape: { + x1: unitX * r + cx, + y1: unitY * r + cy, + x2: unitX * (r - tickLen) + cx, + y2: unitY * (r - tickLen) + cy + }, + silent: true, + style: tickLineStyle + }); + + if (tickLineStyle.stroke === 'auto') { + tickLine.setStyle({ + stroke: getColor((i + j / subSplitNumber) / splitNumber) + }); + } + + group.add(tickLine); + angle += subStep; + } + angle -= subStep; + } + else { + angle += step; + } + } + }, + + _renderPointer: function ( + seriesModel, ecModel, api, getColor, posInfo, + startAngle, endAngle, clockwise + ) { + var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')]; + var angleExtent = [startAngle, endAngle]; + + if (!clockwise) { + angleExtent = angleExtent.reverse(); + } + + var data = seriesModel.getData(); + var oldData = this._data; + + var group = this.group; + + data.diff(oldData) + .add(function (idx) { + var pointer = new PointerPath({ + shape: { + angle: startAngle + } + }); + + graphic.updateProps(pointer, { + shape: { + angle: numberUtil.linearMap(data.get('value', idx), valueExtent, angleExtent, true) + } + }, seriesModel); + + group.add(pointer); + data.setItemGraphicEl(idx, pointer); + }) + .update(function (newIdx, oldIdx) { + var pointer = oldData.getItemGraphicEl(oldIdx); + + graphic.updateProps(pointer, { + shape: { + angle: numberUtil.linearMap(data.get('value', newIdx), valueExtent, angleExtent, true) + } + }, seriesModel); + + group.add(pointer); + data.setItemGraphicEl(newIdx, pointer); + }) + .remove(function (idx) { + var pointer = oldData.getItemGraphicEl(idx); + group.remove(pointer); + }) + .execute(); + + data.eachItemGraphicEl(function (pointer, idx) { + var itemModel = data.getItemModel(idx); + var pointerModel = itemModel.getModel('pointer'); + + pointer.setShape({ + x: posInfo.cx, + y: posInfo.cy, + width: parsePercent( + pointerModel.get('width'), posInfo.r + ), + r: parsePercent(pointerModel.get('length'), posInfo.r) + }); + + pointer.useStyle(itemModel.getModel('itemStyle.normal').getItemStyle()); + + if (pointer.style.fill === 'auto') { + pointer.setStyle('fill', getColor( + (data.get('value', idx) - valueExtent[0]) / (valueExtent[1] - valueExtent[0]) + )); + } + + graphic.setHoverStyle( + pointer, itemModel.getModel('itemStyle.emphasis').getItemStyle() + ); + }); + + this._data = data; + }, + + _renderTitle: function ( + seriesModel, ecModel, api, getColor, posInfo + ) { + var titleModel = seriesModel.getModel('title'); + if (titleModel.get('show')) { + var textStyleModel = titleModel.getModel('textStyle'); + var offsetCenter = titleModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r); + var text = new graphic.Text({ + style: { + x: x, + y: y, + // FIXME First data name ? + text: seriesModel.getData().getName(0), + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont(), + textAlign: 'center', + textVerticalAlign: 'middle' + } + }); + this.group.add(text); + } + }, + + _renderDetail: function ( + seriesModel, ecModel, api, getColor, posInfo + ) { + var detailModel = seriesModel.getModel('detail'); + var minVal = seriesModel.get('min'); + var maxVal = seriesModel.get('max'); + if (detailModel.get('show')) { + var textStyleModel = detailModel.getModel('textStyle'); + var offsetCenter = detailModel.get('offsetCenter'); + var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r); + var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r); + var width = parsePercent(detailModel.get('width'), posInfo.r); + var height = parsePercent(detailModel.get('height'), posInfo.r); + var value = seriesModel.getData().get('value', 0); + var rect = new graphic.Rect({ + shape: { + x: x - width / 2, + y: y - height / 2, + width: width, + height: height + }, + style: { + text: formatLabel( + // FIXME First data name ? + value, detailModel.get('formatter') + ), + fill: detailModel.get('backgroundColor'), + textFill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + } + }); + if (rect.style.textFill === 'auto') { + rect.setStyle('textFill', getColor( + numberUtil.linearMap(value, [minVal, maxVal], [0, 1], true) + )); + } + rect.setStyle(detailModel.getItemStyle(['color'])); + this.group.add(rect); + } + } + }); + + module.exports = GaugeView; + + +/***/ }, +/* 218 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(45).extend({ + + type: 'echartsGaugePointer', + + shape: { + angle: 0, + + width: 10, + + r: 10, + + x: 0, + + y: 0 + }, + + buildPath: function (ctx, shape) { + var mathCos = Math.cos; + var mathSin = Math.sin; + + var r = shape.r; + var width = shape.width; + var angle = shape.angle; + var x = shape.x - mathCos(angle) * width * (width >= r / 3 ? 1 : 2); + var y = shape.y - mathSin(angle) * width * (width >= r / 3 ? 1 : 2); + + angle = shape.angle - Math.PI / 2; + ctx.moveTo(x, y); + ctx.lineTo( + shape.x + mathCos(angle) * width, + shape.y + mathSin(angle) * width + ); + ctx.lineTo( + shape.x + mathCos(shape.angle) * r, + shape.y + mathSin(shape.angle) * r + ); + ctx.lineTo( + shape.x - mathCos(angle) * width, + shape.y - mathSin(angle) * width + ); + ctx.lineTo(x, y); + return; + } + }); + + +/***/ }, +/* 219 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(220); + __webpack_require__(221); + + echarts.registerVisual(zrUtil.curry(__webpack_require__(143), 'funnel')); + echarts.registerLayout(__webpack_require__(222)); + + echarts.registerProcessor(zrUtil.curry(__webpack_require__(146), 'funnel')); + + +/***/ }, +/* 220 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var modelUtil = __webpack_require__(5); + var completeDimensions = __webpack_require__(102); + + var FunnelSeries = __webpack_require__(1).extendSeriesModel({ + + type: 'series.funnel', + + init: function (option) { + FunnelSeries.superApply(this, 'init', arguments); + + // Enable legend selection for each data item + // Use a function instead of direct access because data reference may changed + this.legendDataProvider = function () { + return this._dataBeforeProcessed; + }; + // Extend labelLine emphasis + this._defaultLabelLine(option); + }, + + getInitialData: function (option, ecModel) { + var dimensions = completeDimensions(['value'], option.data); + var list = new List(dimensions, this); + list.initData(option.data); + return list; + }, + + _defaultLabelLine: function (option) { + // Extend labelLine emphasis + modelUtil.defaultEmphasis(option.labelLine, ['show']); + + var labelLineNormalOpt = option.labelLine.normal; + var labelLineEmphasisOpt = option.labelLine.emphasis; + // Not show label line if `label.normal.show = false` + labelLineNormalOpt.show = labelLineNormalOpt.show + && option.label.normal.show; + labelLineEmphasisOpt.show = labelLineEmphasisOpt.show + && option.label.emphasis.show; + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + legendHoverLink: true, + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + + // 默认取数据最小最大值 + // min: 0, + // max: 100, + minSize: '0%', + maxSize: '100%', + sort: 'descending', // 'ascending', 'descending' + gap: 0, + funnelAlign: 'center', + label: { + normal: { + show: true, + position: 'outer' + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + // textStyle: null // 默认使用全局文本样式,详见TEXTSTYLE + }, + emphasis: { + show: true + } + }, + labelLine: { + normal: { + show: true, + length: 20, + lineStyle: { + // color: 各异, + width: 1, + type: 'solid' + } + }, + emphasis: {} + }, + itemStyle: { + normal: { + // color: 各异, + borderColor: '#fff', + borderWidth: 1 + }, + emphasis: { + // color: 各异, + } + } + } + }); + + module.exports = FunnelSeries; + + +/***/ }, +/* 221 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * Piece of pie including Sector, Label, LabelLine + * @constructor + * @extends {module:zrender/graphic/Group} + */ + function FunnelPiece(data, idx) { + + graphic.Group.call(this); + + var polygon = new graphic.Polygon(); + var labelLine = new graphic.Polyline(); + var text = new graphic.Text(); + this.add(polygon); + this.add(labelLine); + this.add(text); + + this.updateData(data, idx, true); + + // Hover to change label and labelLine + function onEmphasis() { + labelLine.ignore = labelLine.hoverIgnore; + text.ignore = text.hoverIgnore; + } + function onNormal() { + labelLine.ignore = labelLine.normalIgnore; + text.ignore = text.normalIgnore; + } + this.on('emphasis', onEmphasis) + .on('normal', onNormal) + .on('mouseover', onEmphasis) + .on('mouseout', onNormal); + } + + var funnelPieceProto = FunnelPiece.prototype; + + function getLabelStyle(data, idx, state, labelModel) { + var textStyleModel = labelModel.getModel('textStyle'); + var position = labelModel.get('position'); + var isLabelInside = position === 'inside' || position === 'inner' || position === 'center'; + return { + fill: textStyleModel.getTextColor() + || (isLabelInside ? '#fff' : data.getItemVisual(idx, 'color')), + textFont: textStyleModel.getFont(), + text: zrUtil.retrieve( + data.hostModel.getFormattedLabel(idx, state), + data.getName(idx) + ) + }; + } + + var opacityAccessPath = ['itemStyle', 'normal', 'opacity']; + funnelPieceProto.updateData = function (data, idx, firstCreate) { + + var polygon = this.childAt(0); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var opacity = data.getItemModel(idx).get(opacityAccessPath); + opacity = opacity == null ? 1 : opacity; + + // Reset style + polygon.useStyle({}); + + if (firstCreate) { + polygon.setShape({ + points: layout.points + }); + polygon.setStyle({ opacity : 0 }); + graphic.initProps(polygon, { + style: { + opacity: opacity + } + }, seriesModel, idx); + } + else { + graphic.updateProps(polygon, { + style: { + opacity: opacity + }, + shape: { + points: layout.points + } + }, seriesModel, idx); + } + + // Update common style + var itemStyleModel = itemModel.getModel('itemStyle'); + var visualColor = data.getItemVisual(idx, 'color'); + + polygon.setStyle( + zrUtil.defaults( + { + lineJoin: 'round', + fill: visualColor + }, + itemStyleModel.getModel('normal').getItemStyle(['opacity']) + ) + ); + polygon.hoverStyle = itemStyleModel.getModel('emphasis').getItemStyle(); + + this._updateLabel(data, idx); + + graphic.setHoverStyle(this); + }; + + funnelPieceProto._updateLabel = function (data, idx) { + + var labelLine = this.childAt(1); + var labelText = this.childAt(2); + + var seriesModel = data.hostModel; + var itemModel = data.getItemModel(idx); + var layout = data.getItemLayout(idx); + var labelLayout = layout.label; + var visualColor = data.getItemVisual(idx, 'color'); + + graphic.updateProps(labelLine, { + shape: { + points: labelLayout.linePoints || labelLayout.linePoints + } + }, seriesModel, idx); + + graphic.updateProps(labelText, { + style: { + x: labelLayout.x, + y: labelLayout.y + } + }, seriesModel, idx); + labelText.attr({ + style: { + textAlign: labelLayout.textAlign, + textVerticalAlign: labelLayout.verticalAlign, + textFont: labelLayout.font + }, + rotation: labelLayout.rotation, + origin: [labelLayout.x, labelLayout.y], + z2: 10 + }); + + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var labelLineModel = itemModel.getModel('labelLine.normal'); + var labelLineHoverModel = itemModel.getModel('labelLine.emphasis'); + + labelText.setStyle(getLabelStyle(data, idx, 'normal', labelModel)); + + labelText.ignore = labelText.normalIgnore = !labelModel.get('show'); + labelText.hoverIgnore = !labelHoverModel.get('show'); + + labelLine.ignore = labelLine.normalIgnore = !labelLineModel.get('show'); + labelLine.hoverIgnore = !labelLineHoverModel.get('show'); + + // Default use item visual color + labelLine.setStyle({ + stroke: visualColor + }); + labelLine.setStyle(labelLineModel.getModel('lineStyle').getLineStyle()); + + labelText.hoverStyle = getLabelStyle(data, idx, 'emphasis', labelHoverModel); + labelLine.hoverStyle = labelLineHoverModel.getModel('lineStyle').getLineStyle(); + }; + + zrUtil.inherits(FunnelPiece, graphic.Group); + + + var Funnel = __webpack_require__(42).extend({ + + type: 'funnel', + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var oldData = this._data; + + var group = this.group; + + data.diff(oldData) + .add(function (idx) { + var funnelPiece = new FunnelPiece(data, idx); + + data.setItemGraphicEl(idx, funnelPiece); + + group.add(funnelPiece); + }) + .update(function (newIdx, oldIdx) { + var piePiece = oldData.getItemGraphicEl(oldIdx); + + piePiece.updateData(data, newIdx); + + group.add(piePiece); + data.setItemGraphicEl(newIdx, piePiece); + }) + .remove(function (idx) { + var piePiece = oldData.getItemGraphicEl(idx); + group.remove(piePiece); + }) + .execute(); + + this._data = data; + }, + + remove: function () { + this.group.removeAll(); + this._data = null; + } + }); + + module.exports = Funnel; + + +/***/ }, +/* 222 */ +/***/ function(module, exports, __webpack_require__) { + + + + var layout = __webpack_require__(21); + var number = __webpack_require__(7); + + var parsePercent = number.parsePercent; + + function getViewRect(seriesModel, api) { + return layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); + } + + function getSortedIndices(data, sort) { + var valueArr = data.mapArray('value', function (val) { + return val; + }); + var indices = []; + var isAscending = sort === 'ascending'; + for (var i = 0, len = data.count(); i < len; i++) { + indices[i] = i; + } + indices.sort(function (a, b) { + return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a]; + }); + return indices; + } + + function labelLayout (data) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var labelPosition = labelModel.get('position'); + + var labelLineModel = itemModel.getModel('labelLine.normal'); + + var layout = data.getItemLayout(idx); + var points = layout.points; + + var isLabelInside = labelPosition === 'inner' + || labelPosition === 'inside' || labelPosition === 'center'; + + var textAlign; + var textX; + var textY; + var linePoints; + + if (isLabelInside) { + textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4; + textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4; + textAlign = 'center'; + linePoints = [ + [textX, textY], [textX, textY] + ]; + } + else { + var x1; + var y1; + var x2; + var labelLineLen = labelLineModel.get('length'); + if (labelPosition === 'left') { + // Left side + x1 = (points[3][0] + points[0][0]) / 2; + y1 = (points[3][1] + points[0][1]) / 2; + x2 = x1 - labelLineLen; + textX = x2 - 5; + textAlign = 'right'; + } + else { + // Right side + x1 = (points[1][0] + points[2][0]) / 2; + y1 = (points[1][1] + points[2][1]) / 2; + x2 = x1 + labelLineLen; + textX = x2 + 5; + textAlign = 'left'; + } + var y2 = y1; + + linePoints = [[x1, y1], [x2, y2]]; + textY = y2; + } + + layout.label = { + linePoints: linePoints, + x: textX, + y: textY, + verticalAlign: 'middle', + textAlign: textAlign, + inside: isLabelInside + }; + }); + } + + module.exports = function (ecModel, api, payload) { + ecModel.eachSeriesByType('funnel', function (seriesModel) { + var data = seriesModel.getData(); + var sort = seriesModel.get('sort'); + var viewRect = getViewRect(seriesModel, api); + var indices = getSortedIndices(data, sort); + + var sizeExtent = [ + parsePercent(seriesModel.get('minSize'), viewRect.width), + parsePercent(seriesModel.get('maxSize'), viewRect.width) + ]; + var dataExtent = data.getDataExtent('value'); + var min = seriesModel.get('min'); + var max = seriesModel.get('max'); + if (min == null) { + min = Math.min(dataExtent[0], 0); + } + if (max == null) { + max = dataExtent[1]; + } + + var funnelAlign = seriesModel.get('funnelAlign'); + var gap = seriesModel.get('gap'); + var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count(); + + var y = viewRect.y; + + var getLinePoints = function (idx, offY) { + // End point index is data.count() and we assign it 0 + var val = data.get('value', idx) || 0; + var itemWidth = number.linearMap(val, [min, max], sizeExtent, true); + var x0; + switch (funnelAlign) { + case 'left': + x0 = viewRect.x; + break; + case 'center': + x0 = viewRect.x + (viewRect.width - itemWidth) / 2; + break; + case 'right': + x0 = viewRect.x + viewRect.width - itemWidth; + break; + } + return [ + [x0, offY], + [x0 + itemWidth, offY] + ]; + }; + + if (sort === 'ascending') { + // From bottom to top + itemHeight = -itemHeight; + gap = -gap; + y += viewRect.height; + indices = indices.reverse(); + } + + for (var i = 0; i < indices.length; i++) { + var idx = indices[i]; + var nextIdx = indices[i + 1]; + var start = getLinePoints(idx, y); + var end = getLinePoints(nextIdx, y + itemHeight); + + y += itemHeight + gap; + + data.setItemLayout(idx, { + points: start.concat(end.slice().reverse()) + }); + } + + labelLayout(data); + }); + }; + + +/***/ }, +/* 223 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(224); + + __webpack_require__(235); + __webpack_require__(236); + + echarts.registerVisual(__webpack_require__(237)); + + + +/***/ }, +/* 224 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(225); + __webpack_require__(228); + __webpack_require__(230); + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + var CLICK_THRESHOLD = 5; // > 4 + + // Parallel view + echarts.extendComponentView({ + type: 'parallel', + + render: function (parallelModel, ecModel, api) { + var zr = api.getZr(); + + if (!this.__onMouseDown) { + // FIXME + // click: mousemove check. otherwise confilct with drag brush. + var mousedownPoint; + zr.on('mousedown', this.__onMouseDown = function (e) { + mousedownPoint = [e.offsetX, e.offsetY]; + }); + zr.on('mouseup', this.__onMouseUp = function (e) { + var point = [e.offsetX, e.offsetY]; + var dist = Math.pow(mousedownPoint[0] - point[0], 2) + + Math.pow(mousedownPoint[1] - point[1], 2); + + if (!parallelModel.get('axisExpandable') || dist > CLICK_THRESHOLD) { + return; + } + + var coordSys = parallelModel.coordinateSystem; + var closestDim = coordSys.findClosestAxisDim(point); + if (closestDim) { + var axisIndex = zrUtil.indexOf(coordSys.dimensions, closestDim); + api.dispatchAction({ + type: 'parallelAxisExpand', + axisExpandCenter: axisIndex + }); + } + }); + } + }, + + dispose: function (ecModel, api) { + api.getZr().off(this.__onMouseDown); + api.getZr().off(this.__onMouseUp); + } + }); + + echarts.registerPreprocessor( + __webpack_require__(234) + ); + + + +/***/ }, +/* 225 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parallel coordinate system creater. + */ + + + var Parallel = __webpack_require__(226); + + function create(ecModel, api) { + var coordSysList = []; + + ecModel.eachComponent('parallel', function (parallelModel, idx) { + var coordSys = new Parallel(parallelModel, ecModel, api); + + coordSys.name = 'parallel_' + idx; + coordSys.resize(parallelModel, api); + + parallelModel.coordinateSystem = coordSys; + coordSys.model = parallelModel; + + coordSysList.push(coordSys); + }); + + // Inject the coordinateSystems into seriesModel + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'parallel') { + var parallelModel = ecModel.queryComponents({ + mainType: 'parallel', + index: seriesModel.get('parallelIndex'), + id: seriesModel.get('parallelId') + })[0]; + seriesModel.coordinateSystem = parallelModel.coordinateSystem; + } + }); + + return coordSysList; + } + + __webpack_require__(26).register('parallel', {create: create}); + + + +/***/ }, +/* 226 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Parallel Coordinates + * + */ + + + var layout = __webpack_require__(21); + var axisHelper = __webpack_require__(114); + var zrUtil = __webpack_require__(4); + var ParallelAxis = __webpack_require__(227); + var graphic = __webpack_require__(43); + var matrix = __webpack_require__(11); + + var each = zrUtil.each; + + var PI = Math.PI; + + function Parallel(parallelModel, ecModel, api) { + + /** + * key: dimension + * @type {Object.} + * @private + */ + this._axesMap = {}; + + /** + * key: dimension + * value: {position: [], rotation, } + * @type {Object.} + * @private + */ + this._axesLayout = {}; + + /** + * Always follow axis order. + * @type {Array.} + * @readOnly + */ + this.dimensions = parallelModel.dimensions; + + /** + * @type {module:zrender/core/BoundingRect} + */ + this._rect; + + /** + * @type {module:echarts/coord/parallel/ParallelModel} + */ + this._model = parallelModel; + + this._init(parallelModel, ecModel, api); + } + + Parallel.prototype = { + + type: 'parallel', + + constructor: Parallel, + + /** + * Initialize cartesian coordinate systems + * @private + */ + _init: function (parallelModel, ecModel, api) { + + var dimensions = parallelModel.dimensions; + var parallelAxisIndex = parallelModel.parallelAxisIndex; + + each(dimensions, function (dim, idx) { + + var axisIndex = parallelAxisIndex[idx]; + var axisModel = ecModel.getComponent('parallelAxis', axisIndex); + + var axis = this._axesMap[dim] = new ParallelAxis( + dim, + axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisIndex + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + + // Inject axis into axisModel + axisModel.axis = axis; + + // Inject axisModel into axis + axis.model = axisModel; + }, this); + }, + + /** + * Update axis scale after data processed + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + update: function (ecModel, api) { + this._updateAxesFromSeries(this._model, ecModel); + }, + + /** + * Update properties from series + * @private + */ + _updateAxesFromSeries: function (parallelModel, ecModel) { + ecModel.eachSeries(function (seriesModel) { + + if (!parallelModel.contains(seriesModel, ecModel)) { + return; + } + + var data = seriesModel.getData(); + + each(this.dimensions, function (dim) { + var axis = this._axesMap[dim]; + axis.scale.unionExtent(data.getDataExtent(dim)); + axisHelper.niceScaleExtent(axis, axis.model); + }, this); + }, this); + }, + + /** + * Resize the parallel coordinate system. + * @param {module:echarts/coord/parallel/ParallelModel} parallelModel + * @param {module:echarts/ExtensionAPI} api + */ + resize: function (parallelModel, api) { + this._rect = layout.getLayoutRect( + parallelModel.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + this._layoutAxes(parallelModel); + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getRect: function () { + return this._rect; + }, + + /** + * @private + */ + _layoutAxes: function (parallelModel) { + var rect = this._rect; + var layout = parallelModel.get('layout'); + var axes = this._axesMap; + var dimensions = this.dimensions; + + var size = [rect.width, rect.height]; + var sizeIdx = layout === 'horizontal' ? 0 : 1; + var layoutLength = size[sizeIdx]; + var axisLength = size[1 - sizeIdx]; + var axisExtent = [0, axisLength]; + + each(axes, function (axis) { + var idx = axis.inverse ? 1 : 0; + axis.setExtent(axisExtent[idx], axisExtent[1 - idx]); + }); + + var axisExpandable = parallelModel.get('axisExpandable'); + var axisExpandWidth = parallelModel.get('axisExpandWidth'); + var axisExpandCenter = parallelModel.get('axisExpandCenter'); + var axisExpandCount = parallelModel.get('axisExpandCount') || 0; + var axisExpandWindow; + + if (axisExpandCenter != null) { + // Clamp + var left = Math.max(0, Math.floor(axisExpandCenter - (axisExpandCount - 1) / 2)); + var right = left + axisExpandCount - 1; + if (right >= dimensions.length) { + right = dimensions.length - 1; + left = Math.max(0, Math.floor(right - axisExpandCount + 1)); + } + axisExpandWindow = [left, right]; + } + + var calcPos = (axisExpandable && axisExpandWindow && axisExpandWidth) + ? function (axisIndex, layoutLength, axisCount) { + var peekIntervalCount = axisExpandWindow[1] - axisExpandWindow[0]; + var otherWidth = ( + layoutLength - axisExpandWidth * peekIntervalCount + ) / (axisCount - 1 - peekIntervalCount); + + var position; + + if (axisIndex < axisExpandWindow[0]) { + position = (axisIndex - 1) * otherWidth; + } + else if (axisIndex <= axisExpandWindow[1]) { + position = axisExpandWindow[0] * otherWidth + + (axisIndex - axisExpandWindow[0]) * axisExpandWidth; + } + else if (axisIndex === axisCount - 1) { + position = layoutLength; + } + else { + position = axisExpandWindow[0] * otherWidth + + peekIntervalCount * axisExpandWidth + + (axisIndex - axisExpandWindow[1]) * otherWidth; + } + + return { + position: position, + axisNameAvailableWidth: ( + axisExpandWindow[0] < axisIndex && axisIndex < axisExpandWindow[1] + ) ? axisExpandWidth : otherWidth + }; + } + : function (axisIndex, layoutLength, axisCount) { + var step = layoutLength / (axisCount - 1); + return { + position: step * axisIndex, + axisNameAvailableWidth: step + }; + }; + + each(dimensions, function (dim, idx) { + var posInfo = calcPos(idx, layoutLength, dimensions.length); + + var positionTable = { + horizontal: { + x: posInfo.position, + y: axisLength + }, + vertical: { + x: 0, + y: posInfo.position + } + }; + var rotationTable = { + horizontal: PI / 2, + vertical: 0 + }; + + var position = [ + positionTable[layout].x + rect.x, + positionTable[layout].y + rect.y + ]; + + var rotation = rotationTable[layout]; + var transform = matrix.create(); + matrix.rotate(transform, transform, rotation); + matrix.translate(transform, transform, position); + + // TODO + // tick等排布信息。 + + // TODO + // 根据axis order 更新 dimensions顺序。 + + this._axesLayout[dim] = { + position: position, + rotation: rotation, + transform: transform, + axisNameAvailableWidth: posInfo.axisNameAvailableWidth, + tickDirection: 1, + labelDirection: 1, + axisExpandWindow: axisExpandWindow + }; + }, this); + }, + + /** + * Get axis by dim. + * @param {string} dim + * @return {module:echarts/coord/parallel/ParallelAxis} [description] + */ + getAxis: function (dim) { + return this._axesMap[dim]; + }, + + /** + * Convert a dim value of a single item of series data to Point. + * @param {*} value + * @param {string} dim + * @return {Array} + */ + dataToPoint: function (value, dim) { + return this.axisCoordToPoint( + this._axesMap[dim].dataToCoord(value), + dim + ); + }, + + /** + * Travel data for one time, get activeState of each data item. + * @param {module:echarts/data/List} data + * @param {Functio} cb param: {string} activeState 'active' or 'inactive' or 'normal' + * {number} dataIndex + * @param {Object} context + */ + eachActiveState: function (data, callback, context) { + var dimensions = this.dimensions; + var axesMap = this._axesMap; + var hasActiveSet = this.hasAxisbrushed(); + + for (var i = 0, len = data.count(); i < len; i++) { + var values = data.getValues(dimensions, i); + var activeState; + + if (!hasActiveSet) { + activeState = 'normal'; + } + else { + activeState = 'active'; + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + var dimName = dimensions[j]; + var state = axesMap[dimName].model.getActiveState(values[j], j); + + if (state === 'inactive') { + activeState = 'inactive'; + break; + } + } + } + + callback.call(context, activeState, i); + } + }, + + /** + * Whether has any activeSet. + * @return {boolean} + */ + hasAxisbrushed: function () { + var dimensions = this.dimensions; + var axesMap = this._axesMap; + var hasActiveSet = false; + + for (var j = 0, lenj = dimensions.length; j < lenj; j++) { + if (axesMap[dimensions[j]].model.getActiveState() !== 'normal') { + hasActiveSet = true; + } + } + + return hasActiveSet; + }, + + /** + * Convert coords of each axis to Point. + * Return point. For example: [10, 20] + * @param {Array.} coords + * @param {string} dim + * @return {Array.} + */ + axisCoordToPoint: function (coord, dim) { + var axisLayout = this._axesLayout[dim]; + return graphic.applyTransform([coord, 0], axisLayout.transform); + }, + + /** + * Get axis layout. + */ + getAxisLayout: function (dim) { + return zrUtil.clone(this._axesLayout[dim]); + }, + + findClosestAxisDim: function (point) { + var axisDim; + var minDist = Infinity; + + zrUtil.each(this._axesLayout, function (axisLayout, dim) { + var localPoint = graphic.applyTransform(point, axisLayout.transform, true); + var extent = this._axesMap[dim].getExtent(); + + if (localPoint[0] < extent[0] || localPoint[0] > extent[1]) { + return; + } + + var dist = Math.abs(localPoint[1]); + if (dist < minDist) { + minDist = dist; + axisDim = dim; + } + }, this); + + return axisDim; + } + + }; + + module.exports = Parallel; + + +/***/ }, +/* 227 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + /** + * @constructor module:echarts/coord/parallel/ParallelAxis + * @extends {module:echarts/coord/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + */ + var ParallelAxis = function (dim, scale, coordExtent, axisType, axisIndex) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * @type {number} + * @readOnly + */ + this.axisIndex = axisIndex; + }; + + ParallelAxis.prototype = { + + constructor: ParallelAxis, + + /** + * Axis model + * @param {module:echarts/coord/parallel/AxisModel} + */ + model: null + + }; + + zrUtil.inherits(ParallelAxis, Axis); + + module.exports = ParallelAxis; + + +/***/ }, +/* 228 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Component = __webpack_require__(19); + + __webpack_require__(229); + + Component.extend({ + + type: 'parallel', + + dependencies: ['parallelAxis'], + + /** + * @type {module:echarts/coord/parallel/Parallel} + */ + coordinateSystem: null, + + /** + * Each item like: 'dim0', 'dim1', 'dim2', ... + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * Coresponding to dimensions. + * @type {Array.} + * @readOnly + */ + parallelAxisIndex: null, + + layoutMode: 'box', + + defaultOption: { + zlevel: 0, + z: 0, + left: 80, + top: 60, + right: 80, + bottom: 60, + // width: {totalWidth} - left - right, + // height: {totalHeight} - top - bottom, + + layout: 'horizontal', // 'horizontal' or 'vertical' + + // FIXME + // naming? + axisExpandable: false, + axisExpandCenter: null, + axisExpandCount: 0, + axisExpandWidth: 50, // FIXME '10%' ? + + parallelAxisDefault: null + }, + + /** + * @override + */ + init: function () { + Component.prototype.init.apply(this, arguments); + + this.mergeOption({}); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var thisOption = this.option; + + newOption && zrUtil.merge(thisOption, newOption, true); + + this._initDimensions(); + }, + + /** + * Whether series or axis is in this coordinate system. + * @param {module:echarts/model/Series|module:echarts/coord/parallel/AxisModel} model + * @param {module:echarts/model/Global} ecModel + */ + contains: function (model, ecModel) { + var parallelIndex = model.get('parallelIndex'); + return parallelIndex != null + && ecModel.getComponent('parallel', parallelIndex) === this; + }, + + setAxisExpand: function (opt) { + zrUtil.each( + ['axisExpandable', 'axisExpandCenter', 'axisExpandCount', 'axisExpandWidth'], + function (name) { + if (opt.hasOwnProperty(name)) { + this.option[name] = opt[name]; + } + }, + this + ); + }, + + /** + * @private + */ + _initDimensions: function () { + var dimensions = this.dimensions = []; + var parallelAxisIndex = this.parallelAxisIndex = []; + + var axisModels = zrUtil.filter(this.dependentModels.parallelAxis, function (axisModel) { + // Can not use this.contains here, because + // initialization has not been completed yet. + return axisModel.get('parallelIndex') === this.componentIndex; + }); + + zrUtil.each(axisModels, function (axisModel) { + dimensions.push('dim' + axisModel.get('dim')); + parallelAxisIndex.push(axisModel.componentIndex); + }); + } + + }); + + + +/***/ }, +/* 229 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentModel = __webpack_require__(19); + var zrUtil = __webpack_require__(4); + var makeStyleMapper = __webpack_require__(15); + var axisModelCreator = __webpack_require__(127); + var numberUtil = __webpack_require__(7); + + var AxisModel = ComponentModel.extend({ + + type: 'baseParallelAxis', + + /** + * @type {module:echarts/coord/parallel/Axis} + */ + axis: null, + + /** + * @type {Array.} + * @readOnly + */ + activeIntervals: [], + + /** + * @return {Object} + */ + getAreaSelectStyle: function () { + return makeStyleMapper( + [ + ['fill', 'color'], + ['lineWidth', 'borderWidth'], + ['stroke', 'borderColor'], + ['width', 'width'], + ['opacity', 'opacity'] + ] + ).call(this.getModel('areaSelectStyle')); + }, + + /** + * The code of this feature is put on AxisModel but not ParallelAxis, + * because axisModel can be alive after echarts updating but instance of + * ParallelAxis having been disposed. this._activeInterval should be kept + * when action dispatched (i.e. legend click). + * + * @param {Array.>} intervals interval.length === 0 + * means set all active. + * @public + */ + setActiveIntervals: function (intervals) { + var activeIntervals = this.activeIntervals = zrUtil.clone(intervals); + + // Normalize + if (activeIntervals) { + for (var i = activeIntervals.length - 1; i >= 0; i--) { + numberUtil.asc(activeIntervals[i]); + } + } + }, + + /** + * @param {number|string} [value] When attempting to detect 'no activeIntervals set', + * value can not be input. + * @return {string} 'normal': no activeIntervals set, + * 'active', + * 'inactive'. + * @public + */ + getActiveState: function (value) { + var activeIntervals = this.activeIntervals; + + if (!activeIntervals.length) { + return 'normal'; + } + + if (value == null) { + return 'inactive'; + } + + for (var i = 0, len = activeIntervals.length; i < len; i++) { + if (activeIntervals[i][0] <= value && value <= activeIntervals[i][1]) { + return 'active'; + } + } + return 'inactive'; + } + + }); + + var defaultOption = { + + type: 'value', + + /** + * @type {Array.} + */ + dim: null, // 0, 1, 2, ... + + // parallelIndex: null, + + areaSelectStyle: { + width: 20, + borderWidth: 1, + borderColor: 'rgba(160,197,232)', + color: 'rgba(160,197,232)', + opacity: 0.3 + }, + + realtime: true, // Whether realtime update view when select. + + z: 10 + }; + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + function getAxisType(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); + } + + axisModelCreator('parallel', AxisModel, getAxisType, defaultOption); + + module.exports = AxisModel; + + +/***/ }, +/* 230 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(225); + __webpack_require__(231); + __webpack_require__(232); + + + +/***/ }, +/* 231 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + /** + * @payload + * @property {string} parallelAxisId + * @property {Array.>} intervals + */ + var actionInfo = { + type: 'axisAreaSelect', + event: 'axisAreaSelected', + update: 'updateVisual' + }; + echarts.registerAction(actionInfo, function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallelAxis', query: payload}, + function (parallelAxisModel) { + parallelAxisModel.axis.model.setActiveIntervals(payload.intervals); + } + ); + }); + + /** + * @payload + */ + echarts.registerAction('parallelAxisExpand', function (payload, ecModel) { + ecModel.eachComponent( + {mainType: 'parallel', query: payload}, + function (parallelModel) { + parallelModel.setAxisExpand(payload); + } + ); + + }); + + +/***/ }, +/* 232 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var AxisBuilder = __webpack_require__(132); + var BrushController = __webpack_require__(233); + var graphic = __webpack_require__(43); + + var elementList = ['axisLine', 'axisLabel', 'axisTick', 'axisName']; + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'parallelAxis', + + /** + * @override + */ + init: function (ecModel, api) { + AxisView.superApply(this, 'init', arguments); + + /** + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)); + }, + + /** + * @override + */ + render: function (axisModel, ecModel, api, payload) { + if (fromAxisAreaSelect(axisModel, ecModel, payload)) { + return; + } + + this.axisModel = axisModel; + this.api = api; + + this.group.removeAll(); + + var oldAxisGroup = this._axisGroup; + this._axisGroup = new graphic.Group(); + this.group.add(this._axisGroup); + + if (!axisModel.get('show')) { + return; + } + + var coordSys = ecModel.getComponent( + 'parallel', axisModel.get('parallelIndex') + ).coordinateSystem; + + var areaSelectStyle = axisModel.getAreaSelectStyle(); + var areaWidth = areaSelectStyle.width; + + var dim = axisModel.axis.dim; + var axisLayout = coordSys.getAxisLayout(dim); + + // Fetch from axisModel by default. + var axisLabelShow; + var axisIndex = zrUtil.indexOf(coordSys.dimensions, dim); + + var axisExpandWindow = axisLayout.axisExpandWindow; + if (axisExpandWindow + && (axisIndex <= axisExpandWindow[0] || axisIndex >= axisExpandWindow[1]) + ) { + axisLabelShow = false; + } + + var builderOpt = zrUtil.extend( + { + axisLabelShow: axisLabelShow, + strokeContainThreshold: areaWidth + }, + axisLayout + ); + + var axisBuilder = new AxisBuilder(axisModel, builderOpt); + + zrUtil.each(elementList, axisBuilder.add, axisBuilder); + + this._axisGroup.add(axisBuilder.getGroup()); + + this._refreshBrushController(builderOpt, areaSelectStyle, axisModel, areaWidth); + + graphic.groupTransition(oldAxisGroup, this._axisGroup, axisModel); + }, + + _refreshBrushController: function (builderOpt, areaSelectStyle, axisModel, areaWidth) { + // After filtering, axis may change, select area needs to be update. + var axis = axisModel.axis; + var coverInfoList = zrUtil.map(axisModel.activeIntervals, function (interval) { + return { + brushType: 'lineX', + panelId: 'pl', + range: [ + axis.dataToCoord(interval[0], true), + axis.dataToCoord(interval[1], true) + ] + }; + }); + + var extent = axis.getExtent(); + var extra = 30; // Arbitrary value. + var rect = { + x: extent[0] - extra, + y: -areaWidth / 2, + width: extent[1] - extent[0] + 2 * extra, + height: areaWidth + }; + + this._brushController + .mount({ + enableGlobalPan: true, + rotation: builderOpt.rotation, + position: builderOpt.position + }) + .setPanels([{ + panelId: 'pl', + rect: rect + }]) + .enableBrush({ + brushType: 'lineX', + brushStyle: areaSelectStyle, + removeOnClick: true + }) + .updateCovers(coverInfoList); + }, + + _onBrush: function (coverInfoList, opt) { + // Do not cache these object, because the mey be changed. + var axisModel = this.axisModel; + var axis = axisModel.axis; + + var intervals = zrUtil.map(coverInfoList, function (coverInfo) { + return [ + axis.coordToData(coverInfo.range[0], true), + axis.coordToData(coverInfo.range[1], true) + ]; + }); + + // If realtime is true, action is not dispatched on drag end, because + // the drag end emits the same params with the last drag move event, + // and may have some delay when using touch pad. + if (!axisModel.option.realtime === opt.isEnd || opt.removeOnClick) { // jshint ignore:line + this.api.dispatchAction({ + type: 'axisAreaSelect', + parallelAxisId: axisModel.id, + intervals: intervals + }); + } + }, + + /** + * @override + */ + dispose: function () { + this._brushController.dispose(); + } + }); + + function fromAxisAreaSelect(axisModel, ecModel, payload) { + return payload + && payload.type === 'axisAreaSelect' + && ecModel.findComponents( + {mainType: 'parallelAxis', query: payload} + )[0] === axisModel; + } + + module.exports = AxisView; + + +/***/ }, +/* 233 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Box selection tool. + * + * @module echarts/component/helper/BrushController + */ + + + + var Eventful = __webpack_require__(33); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var interactionMutex = __webpack_require__(175); + var DataDiffer = __webpack_require__(98); + + var curry = zrUtil.curry; + var each = zrUtil.each; + var map = zrUtil.map; + var mathMin = Math.min; + var mathMax = Math.max; + var mathPow = Math.pow; + + var COVER_Z = 10000; + var UNSELECT_THRESHOLD = 6; + var MIN_RESIZE_LINE_WIDTH = 6; + var MUTEX_RESOURCE_KEY = 'globalPan'; + + var DIRECTION_MAP = { + w: [0, 0], + e: [0, 1], + n: [1, 0], + s: [1, 1] + }; + var CURSOR_MAP = { + w: 'ew', + e: 'ew', + n: 'ns', + s: 'ns', + ne: 'nesw', + sw: 'nesw', + nw: 'nwse', + se: 'nwse' + }; + var DEFAULT_BRUSH_OPT = { + brushStyle: { + lineWidth: 2, + stroke: 'rgba(0,0,0,0.3)', + fill: 'rgba(0,0,0,0.1)' + }, + transformable: true, + brushMode: 'single', + removeOnClick: false + }; + + var baseUID = 0; + + /** + * @alias module:echarts/component/helper/BrushController + * @constructor + * @mixin {module:zrender/mixin/Eventful} + * @event module:echarts/component/helper/BrushController#brush + * params: + * areas: Array., coord relates to container group, + * If no container specified, to global. + * opt { + * isEnd: boolean, + * removeOnClick: boolean + * } + * + * @param {module:zrender/zrender~ZRender} zr + */ + function BrushController(zr) { + + if (true) { + zrUtil.assert(zr); + } + + Eventful.call(this); + + /** + * @type {module:zrender/zrender~ZRender} + * @private + */ + this._zr = zr; + + /** + * @type {module:zrender/container/Group} + * @readOnly + */ + this.group = new graphic.Group(); + + /** + * Only for drawing (after enabledBrush). + * @private + * @type {string} + */ + this._brushType; + + /** + * Only for drawing (after enabledBrush). + * @private + * @type {Object} + */ + this._brushOption; + + /** + * @private + * @type {Object} + */ + this._panels; + + /** + * @private + * @type {Array.} + */ + this._track = []; + + /** + * @private + * @type {boolean} + */ + this._dragging; + + /** + * @private + * @type {Array} + */ + this._covers = []; + + /** + * @private + * @type {moudule:zrender/container/Group} + */ + this._creatingCover; + + /** + * true means global panel + * @private + * @type {module:zrender/container/Group|boolean} + */ + this._creatingPanel; + + /** + * @private + * @type {boolean} + */ + this._enableGlobalPan; + + /** + * @private + * @type {boolean} + */ + if (true) { + this._mounted; + } + + /** + * @private + * @type {string} + */ + this._uid = 'brushController_' + baseUID++; + + /** + * @private + * @type {Object} + */ + this._handlers = {}; + each(mouseHandlers, function (handler, eventName) { + this._handlers[eventName] = zrUtil.bind(handler, this); + }, this); + } + + BrushController.prototype = { + + constructor: BrushController, + + /** + * If set to null/undefined/false, select disabled. + * @param {Object} brushOption + * @param {string|boolean} brushOption.brushType 'line', 'rect', 'polygon' or false + * If pass false/null/undefined, disable brush. + * @param {number} [brushOption.brushMode='single'] 'single' or 'multiple' + * @param {boolean} [brushOption.transformable=true] + * @param {boolean} [brushOption.removeOnClick=false] + * @param {Object} [brushOption.brushStyle] + * @param {number} [brushOption.brushStyle.width] + * @param {number} [brushOption.brushStyle.lineWidth] + * @param {string} [brushOption.brushStyle.stroke] + * @param {string} [brushOption.brushStyle.fill] + */ + enableBrush: function (brushOption) { + if (true) { + zrUtil.assert(this._mounted); + } + + this._brushType && doDisableBrush(this); + brushOption.brushType && doEnableBrush(this, brushOption); + + return this; + }, + + /** + * @param {Array.} panelOpts If not pass, it is global brush. + * Each items: {panelId, rect} + */ + setPanels: function (panelOpts) { + var oldPanels = this._panels || {}; + var newPanels = this._panels = panelOpts && panelOpts.length && {}; + var thisGroup = this.group; + + newPanels && each(panelOpts, function (panelOpt) { + var panelId = panelOpt.panelId; + var panel = oldPanels[panelId]; + if (!panel) { + panel = new graphic.Rect({ + silent: true, + invisible: true + }); + thisGroup.add(panel); + } + panel.attr('shape', panelOpt.rect); + panel.__brushPanelId = panelId; + newPanels[panelId] = panel; + oldPanels[panelId] = null; + }); + + each(oldPanels, function (panel) { + panel && thisGroup.remove(panel); + }); + + return this; + }, + + /** + * @param {Object} [opt] + * @return {boolean} [opt.enableGlobalPan=false] + * @return {boolean} [opt.position=[0, 0]] + * @return {boolean} [opt.rotation=0] + * @return {boolean} [opt.scale=[1, 1]] + */ + mount: function (opt) { + opt = opt || {}; + + if (true) { + this._mounted = true; // should be at first. + } + + this._enableGlobalPan = opt.enableGlobalPan; + + var thisGroup = this.group; + this._zr.add(thisGroup); + + thisGroup.attr({ + position: opt.position || [0, 0], + rotation: opt.rotation || 0, + scale: opt.scale || [1, 1] + }); + + return this; + }, + + eachCover: function (cb, context) { + each(this._covers, cb, context); + }, + + /** + * Update covers. + * @param {Array.} brushOptionList Like: + * [ + * {id: 'xx', brushType: 'line', range: [23, 44], brushStyle, transformable}, + * {id: 'yy', brushType: 'rect', range: [[23, 44], [23, 54]]}, + * ... + * ] + * `brushType` is required in each cover info. + * `id` is not mandatory. + * `brushStyle`, `transformable` is not mandatory, use DEFAULT_BRUSH_OPT by default. + * If brushOptionList is null/undefined, all covers removed. + */ + updateCovers: function (brushOptionList) { + if (true) { + zrUtil.assert(this._mounted); + } + + brushOptionList = zrUtil.map(brushOptionList, function (brushOption) { + return zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); + }); + + var tmpIdPrefix = '\0-brush-index-'; + var oldCovers = this._covers; + var newCovers = this._covers = []; + var controller = this; + var creatingCover = this._creatingCover; + + (new DataDiffer(oldCovers, brushOptionList, oldGetKey, getKey)) + .add(addOrUpdate) + .update(addOrUpdate) + .remove(remove) + .execute(); + + return this; + + function getKey(brushOption, index) { + return (brushOption.id != null ? brushOption.id : tmpIdPrefix + index) + + '-' + brushOption.brushType; + } + + function oldGetKey(cover, index) { + return getKey(cover.__brushOption, index); + } + + function addOrUpdate(newIndex, oldIndex) { + var newBrushOption = brushOptionList[newIndex]; + // Consider setOption in event listener of brushSelect, + // where updating cover when creating should be forbiden. + if (oldIndex != null && oldCovers[oldIndex] === creatingCover) { + newCovers[newIndex] = oldCovers[oldIndex]; + } + else { + var cover = newCovers[newIndex] = oldIndex != null + ? ( + oldCovers[oldIndex].__brushOption = newBrushOption, + oldCovers[oldIndex] + ) + : endCreating(controller, createCover(controller, newBrushOption)); + updateCoverAfterCreation(controller, cover); + } + } + + function remove(oldIndex) { + if (oldCovers[oldIndex] !== creatingCover) { + controller.group.remove(oldCovers[oldIndex]); + } + } + }, + + unmount: function () { + this.enableBrush(false); + + // container may 'removeAll' outside. + clearCovers(this); + this._zr.remove(this.group); + + if (true) { + this._mounted = false; // should be at last. + } + + return this; + }, + + dispose: function () { + this.unmount(); + this.off(); + } + }; + + zrUtil.mixin(BrushController, Eventful); + + + function doEnableBrush(controller, brushOption) { + var zr = controller._zr; + + // Consider roam, which takes globalPan too. + if (!controller._enableGlobalPan) { + interactionMutex.take(zr, MUTEX_RESOURCE_KEY, controller._uid); + } + + each(controller._handlers, function (handler, eventName) { + zr.on(eventName, handler); + }); + + controller._brushType = brushOption.brushType; + controller._brushOption = zrUtil.merge(zrUtil.clone(DEFAULT_BRUSH_OPT), brushOption, true); + } + + function doDisableBrush(controller) { + var zr = controller._zr; + + interactionMutex.release(zr, MUTEX_RESOURCE_KEY, controller._uid); + + each(controller._handlers, function (handler, eventName) { + zr.off(eventName, handler); + }); + + controller._brushType = controller._brushOption = null; + } + + function createCover(controller, brushOption) { + var cover = coverRenderers[brushOption.brushType].createCover(controller, brushOption); + updateZ(cover); + cover.__brushOption = brushOption; + controller.group.add(cover); + return cover; + } + + function endCreating(controller, creatingCover) { + var coverRenderer = getCoverRenderer(creatingCover); + if (coverRenderer.endCreating) { + coverRenderer.endCreating(controller, creatingCover); + updateZ(creatingCover); + } + return creatingCover; + } + + function updateCoverShape(controller, cover) { + var brushOption = cover.__brushOption; + getCoverRenderer(cover).updateCoverShape( + controller, cover, brushOption.range, brushOption + ); + } + + function updateZ(group) { + group.traverse(function (el) { + el.z = COVER_Z; + el.z2 = COVER_Z; // Consider in given container. + }); + } + + function updateCoverAfterCreation(controller, cover) { + getCoverRenderer(cover).updateCommon(controller, cover); + updateCoverShape(controller, cover); + } + + function getCoverRenderer(cover) { + return coverRenderers[cover.__brushOption.brushType]; + } + + function getPanelByPoint(controller, x, y) { + var panels = controller._panels; + if (!panels) { + return true; // Global panel + } + var panel; + each(panels, function (pn) { + pn.contain(x, y) && (panel = pn); + }); + return panel; + } + + function getPanelByCover(controller, cover) { + var panels = controller._panels; + if (!panels) { + return true; // Global panel + } + var panelId = cover.__brushOption.panelId; + // User may give cover without coord sys info, + // which is then treated as global panel. + return panelId != null ? panels[panelId] : true; + } + + function clearCovers(controller) { + var covers = controller._covers; + var originalLength = covers.length; + each(covers, function (cover) { + controller.group.remove(cover); + }, controller); + covers.length = 0; + + return !!originalLength; + } + + function trigger(controller, opt) { + var areas = map(controller._covers, function (cover) { + var brushOption = cover.__brushOption; + var range = zrUtil.clone(brushOption.range); + + return { + brushType: brushOption.brushType, + panelId: brushOption.panelId, + range: range + }; + }); + + controller.trigger('brush', areas, { + isEnd: !!opt.isEnd, + removeOnClick: !!opt.removeOnClick + }); + } + + function shouldShowCover(controller) { + var track = controller._track; + + if (!track.length) { + return false; + } + + var p2 = track[track.length - 1]; + var p1 = track[0]; + var dx = p2[0] - p1[0]; + var dy = p2[1] - p1[1]; + var dist = mathPow(dx * dx + dy * dy, 0.5); + + return dist > UNSELECT_THRESHOLD; + } + + function getTrackEnds(track) { + var tail = track.length - 1; + tail < 0 && (tail = 0); + return [track[0], track[tail]]; + } + + function createBaseRectCover(doDrift, controller, brushOption, edgeNames) { + var cover = new graphic.Group(); + + cover.add(new graphic.Rect({ + name: 'main', + style: makeStyle(brushOption), + silent: true, + draggable: true, + cursor: 'move', + drift: curry(doDrift, controller, cover, 'nswe'), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + + each( + edgeNames, + function (name) { + cover.add(new graphic.Rect({ + name: name, + style: {opacity: 0}, + draggable: true, + silent: true, + invisible: true, + drift: curry(doDrift, controller, cover, name), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + } + ); + + return cover; + } + + function updateBaseRect(controller, cover, localRange, brushOption) { + var lineWidth = brushOption.brushStyle.lineWidth || 0; + var handleSize = mathMax(lineWidth, MIN_RESIZE_LINE_WIDTH); + var x = localRange[0][0]; + var y = localRange[1][0]; + var xa = x - lineWidth / 2; + var ya = y - lineWidth / 2; + var x2 = localRange[0][1]; + var y2 = localRange[1][1]; + var x2a = x2 - handleSize + lineWidth / 2; + var y2a = y2 - handleSize + lineWidth / 2; + var width = x2 - x; + var height = y2 - y; + var widtha = width + lineWidth; + var heighta = height + lineWidth; + + updateRectShape(controller, cover, 'main', x, y, width, height); + + if (brushOption.transformable) { + updateRectShape(controller, cover, 'w', xa, ya, handleSize, heighta); + updateRectShape(controller, cover, 'e', x2a, ya, handleSize, heighta); + updateRectShape(controller, cover, 'n', xa, ya, widtha, handleSize); + updateRectShape(controller, cover, 's', xa, y2a, widtha, handleSize); + + updateRectShape(controller, cover, 'nw', xa, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'ne', x2a, ya, handleSize, handleSize); + updateRectShape(controller, cover, 'sw', xa, y2a, handleSize, handleSize); + updateRectShape(controller, cover, 'se', x2a, y2a, handleSize, handleSize); + } + } + + function updateCommon(controller, cover) { + var brushOption = cover.__brushOption; + var transformable = brushOption.transformable; + + var mainEl = cover.childAt(0); + mainEl.useStyle(makeStyle(brushOption)); + mainEl.attr({ + silent: !transformable, + cursor: transformable ? 'move' : 'default' + }); + + each( + ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'], + function (name) { + var el = cover.childOfName(name); + var globalDir = getGlobalDirection(controller, name); + + el && el.attr({ + silent: !transformable, + invisible: !transformable, + cursor: transformable ? CURSOR_MAP[globalDir] + '-resize' : null + }); + } + ); + } + + function updateRectShape(controller, cover, name, x, y, w, h) { + var el = cover.childOfName(name); + el && el.setShape(pointsToRect( + clipByPanel(controller, cover, [[x, y], [x + w, y + h]]) + )); + } + + function makeStyle(brushOption) { + return zrUtil.defaults({strokeNoScale: true}, brushOption.brushStyle); + } + + function formatRectRange(x, y, x2, y2) { + var min = [mathMin(x, x2), mathMin(y, y2)]; + var max = [mathMax(x, x2), mathMax(y, y2)]; + + return [ + [min[0], max[0]], // x range + [min[1], max[1]] // y range + ]; + } + + function getTransform(controller) { + return graphic.getTransform(controller.group); + } + + function getGlobalDirection(controller, localDirection) { + if (localDirection.length > 1) { + localDirection = localDirection.split(''); + var globalDir = [ + getGlobalDirection(controller, localDirection[0]), + getGlobalDirection(controller, localDirection[1]) + ]; + (globalDir[0] === 'e' || globalDir[0] === 'w') && globalDir.reverse(); + return globalDir.join(''); + } + else { + var map = {w: 'left', e: 'right', n: 'top', s: 'bottom'}; + var inverseMap = {left: 'w', right: 'e', top: 'n', bottom: 's'}; + var globalDir = graphic.transformDirection( + map[localDirection], getTransform(controller) + ); + return inverseMap[globalDir]; + } + } + + function driftRect(toRectRange, fromRectRange, controller, cover, name, dx, dy, e) { + var brushOption = cover.__brushOption; + var rectRange = toRectRange(brushOption.range); + var localDelta = toLocalDelta(controller, dx, dy); + + each(name.split(''), function (namePart) { + var ind = DIRECTION_MAP[namePart]; + rectRange[ind[0]][ind[1]] += localDelta[ind[0]]; + }); + + brushOption.range = fromRectRange(formatRectRange( + rectRange[0][0], rectRange[1][0], rectRange[0][1], rectRange[1][1] + )); + + updateCoverAfterCreation(controller, cover); + trigger(controller, {isEnd: false}); + } + + function driftPolygon(controller, cover, dx, dy, e) { + var range = cover.__brushOption.range; + var localDelta = toLocalDelta(controller, dx, dy); + + each(range, function (point) { + point[0] += localDelta[0]; + point[1] += localDelta[1]; + }); + + updateCoverAfterCreation(controller, cover); + trigger(controller, {isEnd: false}); + } + + function toLocalDelta(controller, dx, dy) { + var thisGroup = controller.group; + var localD = thisGroup.transformCoordToLocal(dx, dy); + var localZero = thisGroup.transformCoordToLocal(0, 0); + + return [localD[0] - localZero[0], localD[1] - localZero[1]]; + } + + function clipByPanel(controller, cover, data) { + var panel = getPanelByCover(controller, cover); + if (panel === true) { // Global panel + return zrUtil.clone(data); + } + + var panelRect = panel.getBoundingRect(); + + return zrUtil.map(data, function (point) { + var x = point[0]; + x = mathMax(x, panelRect.x); + x = mathMin(x, panelRect.x + panelRect.width); + var y = point[1]; + y = mathMax(y, panelRect.y); + y = mathMin(y, panelRect.y + panelRect.height); + return [x, y]; + }); + } + + function pointsToRect(points) { + var xmin = mathMin(points[0][0], points[1][0]); + var ymin = mathMin(points[0][1], points[1][1]); + var xmax = mathMax(points[0][0], points[1][0]); + var ymax = mathMax(points[0][1], points[1][1]); + + return { + x: xmin, + y: ymin, + width: xmax - xmin, + height: ymax - ymin + }; + } + + function resetCursor(controller, e) { + var x = e.offsetX; + var y = e.offsetY; + var zr = controller._zr; + + if (controller._brushType) { // If active + var panels = controller._panels; + var covers = controller._covers; + var inCover; + + for (var i = 0; i < covers.length; i++) { + if (coverRenderers[covers[i].__brushOption.brushType].contain(covers[i], x, y)) { + inCover = true; + break; + } + } + + if (!inCover) { + if (panels) { // Brush on panels + each(panels, function (panel) { + panel.contain(x, y) && zr.setCursorStyle('crosshair'); + }); + } + else { // Global brush + zr.setCursorStyle('crosshair'); + } + } + } + } + + function preventDefault(e) { + var rawE = e.event; + rawE.preventDefault && rawE.preventDefault(); + } + + function mainShapeContain(cover, x, y) { + return cover.childOfName('main').contain(x, y); + } + + function updateCoverByMouse(controller, e, isEnd) { + var x = e.offsetX; + var y = e.offsetY; + var creatingCover = controller._creatingCover; + var panel = controller._creatingPanel; + var thisBrushOption = controller._brushOption; + var eventParams; + + controller._track.push(controller.group.transformCoordToLocal(x, y)); + + if (shouldShowCover(controller) || creatingCover) { + + if (panel && !creatingCover) { + thisBrushOption.brushMode === 'single' && clearCovers(controller); + var brushOption = zrUtil.clone(thisBrushOption); + brushOption.panelId = panel === true ? null : panel.__brushPanelId; + creatingCover = controller._creatingCover = createCover(controller, brushOption); + controller._covers.push(creatingCover); + } + + if (creatingCover) { + var coverRenderer = coverRenderers[controller._brushType]; + var coverBrushOption = creatingCover.__brushOption; + + coverBrushOption.range = coverRenderer.getCreatingRange( + clipByPanel(controller, creatingCover, controller._track) + ); + + if (isEnd) { + endCreating(controller, creatingCover); + coverRenderer.updateCommon(controller, creatingCover); + } + + updateCoverShape(controller, creatingCover); + + eventParams = {isEnd: isEnd}; + } + } + else if ( + isEnd + && thisBrushOption.brushMode === 'single' + && thisBrushOption.removeOnClick + ) { + // Help user to remove covers easily, only by a tiny drag, in 'single' mode. + // But a single click do not clear covers, because user may have casual + // clicks (for example, click on other component and do not expect covers + // disappear). + // Only some cover removed, trigger action, but not every click trigger action. + if (getPanelByPoint(controller, x, y) && clearCovers(controller)) { + eventParams = {isEnd: isEnd, removeOnClick: true}; + } + } + + return eventParams; + } + + var mouseHandlers = { + + mousedown: function (e) { + if (this._dragging) { + // In case some browser do not support globalOut, + // and release mose out side the browser. + handleDragEnd.call(this, e); + } + else if (!e.target || !e.target.draggable) { + + preventDefault(e); + + var x = e.offsetX; + var y = e.offsetY; + + this._creatingCover = null; + var panel = this._creatingPanel = getPanelByPoint(this, x, y); + + if (panel) { + this._dragging = true; + this._track = [this.group.transformCoordToLocal(x, y)]; + } + } + }, + + mousemove: function (e) { + // set Cursor + resetCursor(this, e); + + if (this._dragging) { + + preventDefault(e); + + var eventParams = updateCoverByMouse(this, e, false); + + eventParams && trigger(this, eventParams); + } + }, + + mouseup: handleDragEnd //, + + // FIXME + // in tooltip, globalout should not be triggered. + // globalout: handleDragEnd + }; + + function handleDragEnd(e) { + if (this._dragging) { + + preventDefault(e); + + var eventParams = updateCoverByMouse(this, e, true); + + this._dragging = false; + this._track = []; + this._creatingCover = null; + + // trigger event shoule be at final, after procedure will be nested. + eventParams && trigger(this, eventParams); + } + } + + /** + * key: brushType + * @type {Object} + */ + var coverRenderers = { + + lineX: getLineRenderer(0), + + lineY: getLineRenderer(1), + + rect: { + createCover: function (controller, brushOption) { + return createBaseRectCover( + curry( + driftRect, + function (range) { + return range; + }, + function (range) { + return range; + } + ), + controller, + brushOption, + ['w', 'e', 'n', 's', 'se', 'sw', 'ne', 'nw'] + ); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + return formatRectRange(ends[1][0], ends[1][1], ends[0][0], ends[0][1]); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + updateBaseRect(controller, cover, localRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }, + + polygon: { + createCover: function (controller, brushOption) { + var cover = new graphic.Group(); + + // Do not use graphic.Polygon because graphic.Polyline do not close the + // border of the shape when drawing, which is a better experience for user. + cover.add(new graphic.Polyline({ + name: 'main', + style: makeStyle(brushOption), + silent: true + })); + + return cover; + }, + getCreatingRange: function (localTrack) { + return localTrack; + }, + endCreating: function (controller, cover) { + cover.remove(cover.childAt(0)); + // Use graphic.Polygon close the shape. + cover.add(new graphic.Polygon({ + name: 'main', + draggable: true, + drift: curry(driftPolygon, controller, cover), + ondragend: curry(trigger, controller, {isEnd: true}) + })); + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + cover.childAt(0).setShape({ + points: clipByPanel(controller, cover, localRange) + }); + }, + updateCommon: updateCommon, + contain: mainShapeContain + } + }; + + function getLineRenderer(xyIndex) { + return { + createCover: function (controller, brushOption) { + return createBaseRectCover( + curry( + driftRect, + function (range) { + var rectRange = [range, [0, 100]]; + xyIndex && rectRange.reverse(); + return rectRange; + }, + function (rectRange) { + return rectRange[xyIndex]; + } + ), + controller, + brushOption, + [['w', 'e'], ['n', 's']][xyIndex] + ); + }, + getCreatingRange: function (localTrack) { + var ends = getTrackEnds(localTrack); + var min = mathMin(ends[0][xyIndex], ends[1][xyIndex]); + var max = mathMax(ends[0][xyIndex], ends[1][xyIndex]); + + return [min, max]; + }, + updateCoverShape: function (controller, cover, localRange, brushOption) { + var brushWidth = brushOption.brushStyle.width; + var otherExtent; + // If brushWidth not specified, fit the panel. + if (brushWidth == null) { + var panel = getPanelByCover(controller, cover); + var base = 0; + if (panel !== true) { + var rect = panel.getBoundingRect(); + brushWidth = xyIndex ? rect.width : rect.height; + base = xyIndex ? rect.x : rect.y; + } + // FIXME + // do not support global panel yet. + otherExtent = [base, base + (brushWidth || 0)]; + } + else { + otherExtent = [-brushWidth / 2, brushWidth / 2]; + } + var rectRange = [localRange, otherExtent]; + xyIndex && rectRange.reverse(); + + updateBaseRect(controller, cover, rectRange, brushOption); + }, + updateCommon: updateCommon, + contain: mainShapeContain + }; + } + + module.exports = BrushController; + + +/***/ }, +/* 234 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + module.exports = function (option) { + createParallelIfNeeded(option); + mergeAxisOptionFromParallel(option); + }; + + /** + * Create a parallel coordinate if not exists. + * @inner + */ + function createParallelIfNeeded(option) { + if (option.parallel) { + return; + } + + var hasParallelSeries = false; + + zrUtil.each(option.series, function (seriesOpt) { + if (seriesOpt && seriesOpt.type === 'parallel') { + hasParallelSeries = true; + } + }); + + if (hasParallelSeries) { + option.parallel = [{}]; + } + } + + /** + * Merge aixs definition from parallel option (if exists) to axis option. + * @inner + */ + function mergeAxisOptionFromParallel(option) { + var axes = modelUtil.normalizeToArray(option.parallelAxis); + + zrUtil.each(axes, function (axisOption) { + if (!zrUtil.isObject(axisOption)) { + return; + } + + var parallelIndex = axisOption.parallelIndex || 0; + var parallelOption = modelUtil.normalizeToArray(option.parallel)[parallelIndex]; + + if (parallelOption && parallelOption.parallelAxisDefault) { + zrUtil.merge(axisOption, parallelOption.parallelAxisDefault, false); + } + }); + } + + + +/***/ }, +/* 235 */ +/***/ function(module, exports, __webpack_require__) { + + + + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var completeDimensions = __webpack_require__(102); + + module.exports = SeriesModel.extend({ + + type: 'series.parallel', + + dependencies: ['parallel'], + + getInitialData: function (option, ecModel) { + var parallelModel = ecModel.getComponent( + 'parallel', this.get('parallelIndex') + ); + var parallelAxisIndices = parallelModel.parallelAxisIndex; + + var rawData = option.data; + var modelDims = parallelModel.dimensions; + + var dataDims = generateDataDims(modelDims, rawData); + + var dataDimsInfo = zrUtil.map(dataDims, function (dim, dimIndex) { + + var modelDimsIndex = zrUtil.indexOf(modelDims, dim); + var axisModel = modelDimsIndex >= 0 && ecModel.getComponent( + 'parallelAxis', parallelAxisIndices[modelDimsIndex] + ); + + if (axisModel && axisModel.get('type') === 'category') { + translateCategoryValue(axisModel, dim, rawData); + return {name: dim, type: 'ordinal'}; + } + else if (modelDimsIndex < 0) { + return completeDimensions.guessOrdinal(rawData, dimIndex) + ? {name: dim, type: 'ordinal'} + : dim; + } + else { + return dim; + } + }); + + var list = new List(dataDimsInfo, this); + list.initData(rawData); + + // Anication is forbiden in progressive data mode. + if (this.option.progressive) { + this.option.animation = false; + } + + return list; + }, + + /** + * User can get data raw indices on 'axisAreaSelected' event received. + * + * @public + * @param {string} activeState 'active' or 'inactive' or 'normal' + * @return {Array.} Raw indices + */ + getRawIndicesByActiveState: function (activeState) { + var coordSys = this.coordinateSystem; + var data = this.getData(); + var indices = []; + + coordSys.eachActiveState(data, function (theActiveState, dataIndex) { + if (activeState === theActiveState) { + indices.push(data.getRawIndex(dataIndex)); + } + }); + + return indices; + }, + + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + + coordinateSystem: 'parallel', + parallelIndex: 0, + + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + + inactiveOpacity: 0.05, + activeOpacity: 1, + + lineStyle: { + normal: { + width: 1, + opacity: 0.45, + type: 'solid' + } + }, + progressive: false, // 100 + smooth: false, + + animationEasing: 'linear' + } + }); + + function translateCategoryValue(axisModel, dim, rawData) { + var axisData = axisModel.get('data'); + var numberDim = convertDimNameToNumber(dim); + + if (axisData && axisData.length) { + zrUtil.each(rawData, function (dataItem) { + if (!dataItem) { + return; + } + // FIXME + // time consuming, should use hash? + var index = zrUtil.indexOf(axisData, dataItem[numberDim]); + dataItem[numberDim] = index >= 0 ? index : NaN; + }); + } + // FIXME + // 如果没有设置axis data, 应自动算出,或者提示。 + } + + function convertDimNameToNumber(dimName) { + return +dimName.replace('dim', ''); + } + + function generateDataDims(modelDims, rawData) { + // parallelModel.dimension should not be regarded as data + // dimensions. Consider dimensions = ['dim4', 'dim2', 'dim6']; + + // We detect max dim by parallelModel.dimensions and fist + // item in rawData arbitrarily. + var maxDimNum = 0; + zrUtil.each(modelDims, function (dimName) { + var numberDim = convertDimNameToNumber(dimName); + numberDim > maxDimNum && (maxDimNum = numberDim); + }); + + var firstItem = rawData[0]; + if (firstItem && firstItem.length - 1 > maxDimNum) { + maxDimNum = firstItem.length - 1; + } + + var dataDims = []; + for (var i = 0; i <= maxDimNum; i++) { + dataDims.push('dim' + i); + } + + return dataDims; + } + + +/***/ }, +/* 236 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + var SMOOTH = 0.3; + + var ParallelView = __webpack_require__(42).extend({ + + type: 'parallel', + + init: function () { + + /** + * @type {module:zrender/container/Group} + * @private + */ + this._dataGroup = new graphic.Group(); + + this.group.add(this._dataGroup); + + /** + * @type {module:echarts/data/List} + */ + this._data; + }, + + /** + * @override + */ + render: function (seriesModel, ecModel, api, payload) { + this._renderForNormal(seriesModel); + // this[ + // seriesModel.option.progressive + // ? '_renderForProgressive' + // : '_renderForNormal' + // ](seriesModel); + }, + + /** + * @private + */ + _renderForNormal: function (seriesModel) { + var dataGroup = this._dataGroup; + var data = seriesModel.getData(); + var oldData = this._data; + var coordSys = seriesModel.coordinateSystem; + var dimensions = coordSys.dimensions; + var option = seriesModel.option; + var smooth = option.smooth ? SMOOTH : null; + + // Consider switch between progressive and not. + // oldData && oldData.__plProgressive && dataGroup.removeAll(); + + data.diff(oldData) + .add(add) + .update(update) + .remove(remove) + .execute(); + + // Update style + updateElCommon(data, smooth); + + // First create + if (!this._data) { + var clipPath = createGridClipShape( + coordSys, seriesModel, function () { + // Callback will be invoked immediately if there is no animation + setTimeout(function () { + dataGroup.removeClipPath(); + }); + } + ); + dataGroup.setClipPath(clipPath); + } + + this._data = data; + + function add(newDataIndex) { + addEl(data, dataGroup, newDataIndex, dimensions, coordSys, null, smooth); + } + + function update(newDataIndex, oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + var points = createLinePoints(data, newDataIndex, dimensions, coordSys); + data.setItemGraphicEl(newDataIndex, line); + graphic.updateProps(line, {shape: {points: points}}, seriesModel, newDataIndex); + } + + function remove(oldDataIndex) { + var line = oldData.getItemGraphicEl(oldDataIndex); + dataGroup.remove(line); + } + + }, + + /** + * @private + */ + // _renderForProgressive: function (seriesModel) { + // var dataGroup = this._dataGroup; + // var data = seriesModel.getData(); + // var oldData = this._data; + // var coordSys = seriesModel.coordinateSystem; + // var dimensions = coordSys.dimensions; + // var option = seriesModel.option; + // var progressive = option.progressive; + // var smooth = option.smooth ? SMOOTH : null; + + // // In progressive animation is disabled, so use simple data diff, + // // which effects performance less. + // // (Typically performance for data with length 7000+ like: + // // simpleDiff: 60ms, addEl: 184ms, + // // in RMBP 2.4GHz intel i7, OSX 10.9 chrome 50.0.2661.102 (64-bit)) + // if (simpleDiff(oldData, data, dimensions)) { + // dataGroup.removeAll(); + // data.each(function (dataIndex) { + // addEl(data, dataGroup, dataIndex, dimensions, coordSys); + // }); + // } + + // updateElCommon(data, progressive, smooth); + + // // Consider switch between progressive and not. + // data.__plProgressive = true; + // this._data = data; + // }, + + /** + * @override + */ + remove: function () { + this._dataGroup && this._dataGroup.removeAll(); + this._data = null; + } + }); + + function createGridClipShape(coordSys, seriesModel, cb) { + var parallelModel = coordSys.model; + var rect = coordSys.getRect(); + var rectEl = new graphic.Rect({ + shape: { + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + } + }); + + var dim = parallelModel.get('layout') === 'horizontal' ? 'width' : 'height'; + rectEl.setShape(dim, 0); + graphic.initProps(rectEl, { + shape: { + width: rect.width, + height: rect.height + } + }, seriesModel, cb); + return rectEl; + } + + function createLinePoints(data, dataIndex, dimensions, coordSys) { + var points = []; + for (var i = 0; i < dimensions.length; i++) { + var dimName = dimensions[i]; + var value = data.get(dimName, dataIndex); + if (!isEmptyValue(value, coordSys.getAxis(dimName).type)) { + points.push(coordSys.dataToPoint(value, dimName)); + } + } + return points; + } + + function addEl(data, dataGroup, dataIndex, dimensions, coordSys) { + var points = createLinePoints(data, dataIndex, dimensions, coordSys); + var line = new graphic.Polyline({ + shape: {points: points}, + silent: true, + z2: 10 + }); + dataGroup.add(line); + data.setItemGraphicEl(dataIndex, line); + } + + function updateElCommon(data, smooth) { + var seriesStyleModel = data.hostModel.getModel('lineStyle.normal'); + var lineStyle = seriesStyleModel.getLineStyle(); + data.eachItemGraphicEl(function (line, dataIndex) { + if (data.hasItemOption) { + var itemModel = data.getItemModel(dataIndex); + var lineStyleModel = itemModel.getModel('lineStyle.normal', seriesStyleModel); + lineStyle = lineStyleModel.getLineStyle(); + } + + line.useStyle(zrUtil.extend( + lineStyle, + { + fill: null, + stroke: data.getItemVisual(dataIndex, 'color'), + opacity: data.getItemVisual(dataIndex, 'opacity') + } + )); + line.shape.smooth = smooth; + }); + } + + // function simpleDiff(oldData, newData, dimensions) { + // var oldLen; + // if (!oldData + // || !oldData.__plProgressive + // || (oldLen = oldData.count()) !== newData.count() + // ) { + // return true; + // } + + // var dimLen = dimensions.length; + // for (var i = 0; i < oldLen; i++) { + // for (var j = 0; j < dimLen; j++) { + // if (oldData.get(dimensions[j], i) !== newData.get(dimensions[j], i)) { + // return true; + // } + // } + // } + + // return false; + // } + + // FIXME + // 公用方法? + function isEmptyValue(val, axisType) { + return axisType === 'category' + ? val == null + : (val == null || isNaN(val)); // axisType === 'value' + } + + module.exports = ParallelView; + + +/***/ }, +/* 237 */ +/***/ function(module, exports) { + + + + module.exports = function (ecModel) { + + ecModel.eachSeriesByType('parallel', function (seriesModel) { + + var itemStyleModel = seriesModel.getModel('itemStyle.normal'); + var lineStyleModel = seriesModel.getModel('lineStyle.normal'); + var globalColors = ecModel.get('color'); + + var color = lineStyleModel.get('color') + || itemStyleModel.get('color') + || globalColors[seriesModel.seriesIndex % globalColors.length]; + var inactiveOpacity = seriesModel.get('inactiveOpacity'); + var activeOpacity = seriesModel.get('activeOpacity'); + var lineStyle = seriesModel.getModel('lineStyle.normal').getLineStyle(); + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + + var opacityMap = { + normal: lineStyle.opacity, + active: activeOpacity, + inactive: inactiveOpacity + }; + + coordSys.eachActiveState(data, function (activeState, dataIndex) { + data.setItemVisual(dataIndex, 'opacity', opacityMap[activeState]); + }); + + data.setVisual('color', color); + }); + }; + + +/***/ }, +/* 238 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(239); + __webpack_require__(240); + echarts.registerLayout(__webpack_require__(241)); + echarts.registerVisual(__webpack_require__(243)); + + +/***/ }, +/* 239 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Get initial data and define sankey view's series model + * @author Deqing Li(annong035@gmail.com) + */ + + + var SeriesModel = __webpack_require__(28); + var createGraphFromNodeEdge = __webpack_require__(196); + + var SankeySeries = SeriesModel.extend({ + + type: 'series.sankey', + + layoutInfo: null, + + /** + * Init a graph data structure from data in option series + * + * @param {Object} option the object used to config echarts view + * @return {module:echarts/data/List} storage initial data + */ + getInitialData: function (option) { + var links = option.edges || option.links; + var nodes = option.data || option.nodes; + if (nodes && links) { + var graph = createGraphFromNodeEdge(nodes, links, this, true); + return graph.data; + } + }, + + /** + * Return the graphic data structure + * + * @return {module:echarts/data/Graph} graphic data structure + */ + getGraph: function () { + return this.getData().graph; + }, + + /** + * Get edge data of graphic data structure + * + * @return {module:echarts/data/List} data structure of list + */ + getEdgeData: function () { + return this.getGraph().edgeData; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, multipleSeries, dataType) { + // dataType === 'node' or empty do not show tooltip by default + if (dataType === 'edge') { + var params = this.getDataParams(dataIndex, dataType); + var rawDataOpt = params.data; + var html = rawDataOpt.source + ' -- ' + rawDataOpt.target; + if (params.value) { + html += ' : ' + params.value; + } + return html; + } + + return SankeySeries.superCall(this, 'formatTooltip', dataIndex, multipleSeries); + }, + + defaultOption: { + zlevel: 0, + z: 2, + + coordinateSystem: 'view', + + layout: null, + + // the position of the whole view + left: '5%', + top: '5%', + right: '20%', + bottom: '5%', + + // the dx of the node + nodeWidth: 20, + + // the vertical distance between two nodes + nodeGap: 8, + + // the number of iterations to change the position of the node + layoutIterations: 32, + + label: { + normal: { + show: true, + position: 'right', + textStyle: { + color: '#000', + fontSize: 12 + } + }, + emphasis: { + show: true + } + }, + + itemStyle: { + normal: { + borderWidth: 1, + borderColor: '#333' + } + }, + + lineStyle: { + normal: { + color: '#314656', + opacity: 0.2, + curveness: 0.5 + }, + emphasis: { + opacity: 0.6 + } + }, + + animationEasing: 'linear', + + animationDuration: 1000 + } + + }); + + module.exports = SankeySeries; + + + +/***/ }, +/* 240 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file The file used to draw sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + var SankeyShape = graphic.extendShape({ + shape: { + x1: 0, y1: 0, + x2: 0, y2: 0, + cpx1: 0, cpy1: 0, + cpx2: 0, cpy2: 0, + + extent: 0 + }, + + buildPath: function (ctx, shape) { + var halfExtent = shape.extent / 2; + ctx.moveTo(shape.x1, shape.y1 - halfExtent); + ctx.bezierCurveTo( + shape.cpx1, shape.cpy1 - halfExtent, + shape.cpx2, shape.cpy2 - halfExtent, + shape.x2, shape.y2 - halfExtent + ); + ctx.lineTo(shape.x2, shape.y2 + halfExtent); + ctx.bezierCurveTo( + shape.cpx2, shape.cpy2 + halfExtent, + shape.cpx1, shape.cpy1 + halfExtent, + shape.x1, shape.y1 + halfExtent + ); + ctx.closePath(); + } + }); + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'sankey', + + /** + * @private + * @type {module:echarts/chart/sankey/SankeySeries} + */ + _model: null, + + render: function (seriesModel, ecModel, api) { + var graph = seriesModel.getGraph(); + var group = this.group; + var layoutInfo = seriesModel.layoutInfo; + var nodeData = seriesModel.getData(); + var edgeData = seriesModel.getData('edge'); + + this._model = seriesModel; + + group.removeAll(); + + group.position = [layoutInfo.x, layoutInfo.y]; + + // generate a bezire Curve for each edge + graph.eachEdge(function (edge) { + var curve = new SankeyShape(); + + curve.dataIndex = edge.dataIndex; + curve.seriesIndex = seriesModel.seriesIndex; + curve.dataType = 'edge'; + + var lineStyleModel = edge.getModel('lineStyle.normal'); + var curvature = lineStyleModel.get('curveness'); + var n1Layout = edge.node1.getLayout(); + var n2Layout = edge.node2.getLayout(); + var edgeLayout = edge.getLayout(); + + curve.shape.extent = Math.max(1, edgeLayout.dy); + + var x1 = n1Layout.x + n1Layout.dx; + var y1 = n1Layout.y + edgeLayout.sy + edgeLayout.dy / 2; + var x2 = n2Layout.x; + var y2 = n2Layout.y + edgeLayout.ty + edgeLayout.dy / 2; + var cpx1 = x1 * (1 - curvature) + x2 * curvature; + var cpy1 = y1; + var cpx2 = x1 * curvature + x2 * (1 - curvature); + var cpy2 = y2; + + curve.setShape({ + x1: x1, + y1: y1, + x2: x2, + y2: y2, + cpx1: cpx1, + cpy1: cpy1, + cpx2: cpx2, + cpy2: cpy2 + }); + + curve.setStyle(lineStyleModel.getItemStyle()); + // Special color, use source node color or target node color + switch (curve.style.fill) { + case 'source': + curve.style.fill = edge.node1.getVisual('color'); + break; + case 'target': + curve.style.fill = edge.node2.getVisual('color'); + break; + } + + graphic.setHoverStyle(curve, edge.getModel('lineStyle.emphasis').getItemStyle()); + + group.add(curve); + + edgeData.setItemGraphicEl(edge.dataIndex, curve); + }); + + // generate a rect for each node + graph.eachNode(function (node) { + var layout = node.getLayout(); + var itemModel = node.getModel(); + var labelModel = itemModel.getModel('label.normal'); + var textStyleModel = labelModel.getModel('textStyle'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var textStyleHoverModel = labelHoverModel.getModel('textStyle'); + + var rect = new graphic.Rect({ + shape: { + x: layout.x, + y: layout.y, + width: node.getLayout().dx, + height: node.getLayout().dy + }, + style: { + // Get formatted label in label.normal option + // Use node id if it is not specified + text: labelModel.get('show') + ? seriesModel.getFormattedLabel(node.dataIndex, 'normal') || node.id + // Use empty string to hide the label + : '', + textFont: textStyleModel.getFont(), + textFill: textStyleModel.getTextColor(), + textPosition: labelModel.get('position') + } + }); + + rect.setStyle(zrUtil.defaults( + { + fill: node.getVisual('color') + }, + itemModel.getModel('itemStyle.normal').getItemStyle() + )); + + graphic.setHoverStyle(rect, zrUtil.extend( + node.getModel('itemStyle.emphasis'), + { + text: labelHoverModel.get('show') + ? seriesModel.getFormattedLabel(node.dataIndex, 'emphasis') || node.id + : '', + textFont: textStyleHoverModel.getFont(), + textFill: textStyleHoverModel.getTextColor(), + textPosition: labelHoverModel.get('position') + } + )); + + group.add(rect); + + nodeData.setItemGraphicEl(node.dataIndex, rect); + + rect.dataType = 'node'; + }); + + if (!this._data && seriesModel.get('animation')) { + group.setClipPath(createGridClipShape(group.getBoundingRect(), seriesModel, function () { + group.removeClipPath(); + })); + } + + this._data = seriesModel.getData(); + } + }); + + // add animation to the view + function createGridClipShape(rect, seriesModel, cb) { + var rectEl = new graphic.Rect({ + shape: { + x: rect.x - 10, + y: rect.y - 10, + width: 0, + height: rect.height + 20 + } + }); + graphic.initProps(rectEl, { + shape: { + width: rect.width + 20, + height: rect.height + 20 + } + }, seriesModel, cb); + + return rectEl; + } + + + +/***/ }, +/* 241 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file The layout algorithm of sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var layout = __webpack_require__(21); + var nest = __webpack_require__(242); + var zrUtil = __webpack_require__(4); + + module.exports = function (ecModel, api, payload) { + + ecModel.eachSeriesByType('sankey', function (seriesModel) { + + var nodeWidth = seriesModel.get('nodeWidth'); + var nodeGap = seriesModel.get('nodeGap'); + + var layoutInfo = getViewRect(seriesModel, api); + + seriesModel.layoutInfo = layoutInfo; + + var width = layoutInfo.width; + var height = layoutInfo.height; + + var graph = seriesModel.getGraph(); + + var nodes = graph.nodes; + var edges = graph.edges; + + computeNodeValues(nodes); + + var filteredNodes = nodes.filter(function (node) { + return node.getLayout().value === 0; + }); + + var iterations = filteredNodes.length !== 0 + ? 0 : seriesModel.get('layoutIterations'); + + layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations); + }); + }; + + /** + * Get the layout position of the whole view + * + * @param {module:echarts/model/Series} seriesModel the model object of sankey series + * @param {module:echarts/ExtensionAPI} api provide the API list that the developer can call + * @return {module:zrender/core/BoundingRect} size of rect to draw the sankey view + */ + function getViewRect(seriesModel, api) { + return layout.getLayoutRect( + seriesModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + } + ); + } + + function layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations) { + computeNodeBreadths(nodes, nodeWidth, width); + computeNodeDepths(nodes, edges, height, nodeGap, iterations); + computeEdgeDepths(nodes); + } + + /** + * Compute the value of each node by summing the associated edge's value + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + */ + function computeNodeValues(nodes) { + zrUtil.each(nodes, function (node) { + var value1 = sum(node.outEdges, getEdgeValue); + var value2 = sum(node.inEdges, getEdgeValue); + var value = Math.max(value1, value2); + node.setLayout({value: value}, true); + }); + } + + /** + * Compute the x-position for each node + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} nodeWidth the dx of the node + * @param {number} width the whole width of the area to draw the view + */ + function computeNodeBreadths(nodes, nodeWidth, width) { + var remainNodes = nodes; + var nextNode = null; + var x = 0; + var kx = 0; + + while (remainNodes.length) { + nextNode = []; + for (var i = 0, len = remainNodes.length; i < len; i++) { + var node = remainNodes[i]; + node.setLayout({x: x}, true); + node.setLayout({dx: nodeWidth}, true); + for (var j = 0, lenj = node.outEdges.length; j < lenj; j++) { + nextNode.push(node.outEdges[j].node2); + } + } + remainNodes = nextNode; + ++x; + } + + moveSinksRight(nodes, x); + kx = (width - nodeWidth) / (x - 1); + + scaleNodeBreadths(nodes, kx); + } + + /** + * All the node without outEgdes are assigned maximum x-position and + * be aligned in the last column. + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} x value (x-1) use to assign to node without outEdges + * as x-position + */ + function moveSinksRight(nodes, x) { + zrUtil.each(nodes, function (node) { + if (!node.outEdges.length) { + node.setLayout({x: x - 1}, true); + } + }); + } + + /** + * Scale node x-position to the width + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {number} kx multiple used to scale nodes + */ + function scaleNodeBreadths(nodes, kx) { + zrUtil.each(nodes, function (node) { + var nodeX = node.getLayout().x * kx; + node.setLayout({x: nodeX}, true); + }); + } + + /** + * Using Gauss-Seidel iterations method to compute the node depth(y-position) + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {module:echarts/data/Graph~Edge} edges edge of sankey view + * @param {number} height the whole height of the area to draw the view + * @param {numbber} nodeGap the vertical distance between two nodes + * in the same column. + * @param {number} iterations the number of iterations for the algorithm + */ + function computeNodeDepths(nodes, edges, height, nodeGap, iterations) { + var nodesByBreadth = nest() + .key(function (d) { + return d.getLayout().x; + }) + .sortKeys(ascending) + .entries(nodes) + .map(function (d) { + return d.values; + }); + + initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap); + resolveCollisions(nodesByBreadth, nodeGap, height); + + for (var alpha = 1; iterations > 0; iterations--) { + // 0.99 is a experience parameter, ensure that each iterations of + // changes as small as possible. + alpha *= 0.99; + relaxRightToLeft(nodesByBreadth, alpha); + resolveCollisions(nodesByBreadth, nodeGap, height); + relaxLeftToRight(nodesByBreadth, alpha); + resolveCollisions(nodesByBreadth, nodeGap, height); + } + } + + /** + * Compute the original y-position for each node + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the nodes x-position. + * @param {module:echarts/data/Graph~Edge} edges edge of sankey view + * @param {number} height the whole height of the area to draw the view + * @param {number} nodeGap the vertical distance between two nodes + */ + function initializeNodeDepth(nodes, nodesByBreadth, edges, height, nodeGap) { + var kyArray = []; + zrUtil.each(nodesByBreadth, function (nodes) { + var n = nodes.length; + var sum = 0; + zrUtil.each(nodes, function (node) { + sum += node.getLayout().value; + }); + var ky = (height - (n - 1) * nodeGap) / sum; + kyArray.push(ky); + }); + + kyArray.sort(function (a, b) { + return a - b; + }); + var ky0 = kyArray[0]; + + zrUtil.each(nodesByBreadth, function (nodes) { + zrUtil.each(nodes, function (node, i) { + node.setLayout({y: i}, true); + var nodeDy = node.getLayout().value * ky0; + node.setLayout({dy: nodeDy}, true); + }); + }); + + zrUtil.each(edges, function (edge) { + var edgeDy = +edge.getValue() * ky0; + edge.setLayout({dy: edgeDy}, true); + }); + } + + /** + * Resolve the collision of initialized depth (y-position) + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the nodes x-position. + * @param {number} nodeGap the vertical distance between two nodes + * @param {number} height the whole height of the area to draw the view + */ + function resolveCollisions(nodesByBreadth, nodeGap, height) { + zrUtil.each(nodesByBreadth, function (nodes) { + var node; + var dy; + var y0 = 0; + var n = nodes.length; + var i; + + nodes.sort(ascendingDepth); + + for (i = 0; i < n; i++) { + node = nodes[i]; + dy = y0 - node.getLayout().y; + if (dy > 0) { + var nodeY = node.getLayout().y + dy; + node.setLayout({y: nodeY}, true); + } + y0 = node.getLayout().y + node.getLayout().dy + nodeGap; + } + + // if the bottommost node goes outside the bounds, push it back up + dy = y0 - nodeGap - height; + if (dy > 0) { + var nodeY = node.getLayout().y - dy; + node.setLayout({y: nodeY}, true); + y0 = node.getLayout().y; + for (i = n - 2; i >= 0; --i) { + node = nodes[i]; + dy = node.getLayout().y + node.getLayout().dy + nodeGap - y0; + if (dy > 0) { + nodeY = node.getLayout().y - dy; + node.setLayout({y: nodeY}, true); + } + y0 = node.getLayout().y; + } + } + }); + } + + /** + * Change the y-position of the nodes, except most the right side nodes + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the node x-position. + * @param {number} alpha parameter used to adjust the nodes y-position + */ + function relaxRightToLeft(nodesByBreadth, alpha) { + zrUtil.each(nodesByBreadth.slice().reverse(), function (nodes) { + zrUtil.each(nodes, function (node) { + if (node.outEdges.length) { + var y = sum(node.outEdges, weightedTarget) / sum(node.outEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); + } + + function weightedTarget(edge) { + return center(edge.node2) * edge.getValue(); + } + + /** + * Change the y-position of the nodes, except most the left side nodes + * + * @param {Array.>} nodesByBreadth + * group by the array of all sankey nodes based on the node x-position. + * @param {number} alpha parameter used to adjust the nodes y-position + */ + function relaxLeftToRight(nodesByBreadth, alpha) { + zrUtil.each(nodesByBreadth, function (nodes) { + zrUtil.each(nodes, function (node) { + if (node.inEdges.length) { + var y = sum(node.inEdges, weightedSource) / sum(node.inEdges, getEdgeValue); + var nodeY = node.getLayout().y + (y - center(node)) * alpha; + node.setLayout({y: nodeY}, true); + } + }); + }); + } + + function weightedSource(edge) { + return center(edge.node1) * edge.getValue(); + } + + /** + * Compute the depth(y-position) of each edge + * + * @param {module:echarts/data/Graph~Node} nodes node of sankey view + */ + function computeEdgeDepths(nodes) { + zrUtil.each(nodes, function (node) { + node.outEdges.sort(ascendingTargetDepth); + node.inEdges.sort(ascendingSourceDepth); + }); + zrUtil.each(nodes, function (node) { + var sy = 0; + var ty = 0; + zrUtil.each(node.outEdges, function (edge) { + edge.setLayout({sy: sy}, true); + sy += edge.getLayout().dy; + }); + zrUtil.each(node.inEdges, function (edge) { + edge.setLayout({ty: ty}, true); + ty += edge.getLayout().dy; + }); + }); + } + + function ascendingTargetDepth(a, b) { + return a.node2.getLayout().y - b.node2.getLayout().y; + } + + function ascendingSourceDepth(a, b) { + return a.node1.getLayout().y - b.node1.getLayout().y; + } + + function sum(array, f) { + var sum = 0; + var len = array.length; + var i = -1; + while (++i < len) { + var value = +f.call(array, array[i], i); + if (!isNaN(value)) { + sum += value; + } + } + return sum; + } + + function center(node) { + return node.getLayout().y + node.getLayout().dy / 2; + } + + function ascendingDepth(a, b) { + return a.getLayout().y - b.getLayout().y; + } + + function ascending(a, b) { + return a < b ? -1 : a > b ? 1 : a === b ? 0 : NaN; + } + + function getEdgeValue(edge) { + return edge.getValue(); + } + + + +/***/ }, +/* 242 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + /** + * nest helper used to group by the array. + * can specified the keys and sort the keys. + */ + function nest() { + + var keysFunction = []; + var sortKeysFunction = []; + + /** + * map an Array into the mapObject. + * @param {Array} array + * @param {number} depth + */ + function map(array, depth) { + if (depth >= keysFunction.length) { + return array; + } + var i = -1; + var n = array.length; + var keyFunction = keysFunction[depth++]; + var mapObject = {}; + var valuesByKey = {}; + + while (++i < n) { + var keyValue = keyFunction(array[i]); + var values = valuesByKey[keyValue]; + + if (values) { + values.push(array[i]); + } + else { + valuesByKey[keyValue] = [array[i]]; + } + } + + zrUtil.each(valuesByKey, function (value, key) { + mapObject[key] = map(value, depth); + }); + + return mapObject; + } + + /** + * transform the Map Object to multidimensional Array + * @param {Object} map + * @param {number} depth + */ + function entriesMap(mapObject, depth) { + if (depth >= keysFunction.length) { + return mapObject; + } + var array = []; + var sortKeyFunction = sortKeysFunction[depth++]; + + zrUtil.each(mapObject, function (value, key) { + array.push({ + key: key, values: entriesMap(value, depth) + }); + }); + + if (sortKeyFunction) { + return array.sort(function (a, b) { + return sortKeyFunction(a.key, b.key); + }); + } + else { + return array; + } + } + + return { + /** + * specified the key to groupby the arrays. + * users can specified one more keys. + * @param {Function} d + */ + key: function (d) { + keysFunction.push(d); + return this; + }, + + /** + * specified the comparator to sort the keys + * @param {Function} order + */ + sortKeys: function (order) { + sortKeysFunction[keysFunction.length - 1] = order; + return this; + }, + + /** + * the array to be grouped by. + * @param {Array} array + */ + entries: function (array) { + return entriesMap(map(array, 0), 0); + } + }; + } + module.exports = nest; + + +/***/ }, +/* 243 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual encoding for sankey view + * @author Deqing Li(annong035@gmail.com) + */ + + + var VisualMapping = __webpack_require__(192); + + module.exports = function (ecModel, payload) { + ecModel.eachSeriesByType('sankey', function (seriesModel) { + var graph = seriesModel.getGraph(); + var nodes = graph.nodes; + + nodes.sort(function (a, b) { + return a.getLayout().value - b.getLayout().value; + }); + + var minValue = nodes[0].getLayout().value; + var maxValue = nodes[nodes.length - 1].getLayout().value; + + nodes.forEach(function (node) { + var mapping = new VisualMapping({ + type: 'color', + mappingMethod: 'linear', + dataExtent: [minValue, maxValue], + visual: seriesModel.get('color') + }); + + var mapValueToColor = mapping.mapValueToVisual(node.getLayout().value); + node.setVisual('color', mapValueToColor); + // If set itemStyle.normal.color + var itemModel = node.getModel(); + var customColor = itemModel.get('itemStyle.normal.color'); + if (customColor != null) { + node.setVisual('color', customColor); + } + }); + + }); + }; + + + +/***/ }, +/* 244 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(245); + __webpack_require__(248); + + echarts.registerVisual(__webpack_require__(249)); + echarts.registerLayout(__webpack_require__(250)); + + + +/***/ }, +/* 245 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var whiskerBoxCommon = __webpack_require__(246); + + var BoxplotSeries = SeriesModel.extend({ + + type: 'series.boxplot', + + dependencies: ['xAxis', 'yAxis', 'grid'], + + // TODO + // box width represents group size, so dimension should have 'size'. + + /** + * @see + * The meanings of 'min' and 'max' depend on user, + * and echarts do not need to know it. + * @readOnly + */ + valueDimensions: ['min', 'Q1', 'median', 'Q3', 'max'], + + /** + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * @override + */ + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + + // xAxisIndex: 0, + // yAxisIndex: 0, + + layout: null, // 'horizontal' or 'vertical' + boxWidth: [7, 50], // [min, max] can be percent of band width. + + itemStyle: { + normal: { + color: '#fff', + borderWidth: 1 + }, + emphasis: { + borderWidth: 2, + shadowBlur: 5, + shadowOffsetX: 2, + shadowOffsetY: 2, + shadowColor: 'rgba(0,0,0,0.4)' + } + }, + + animationEasing: 'elasticOut', + animationDuration: 800 + } + }); + + zrUtil.mixin(BoxplotSeries, whiskerBoxCommon.seriesModelMixin, true); + + module.exports = BoxplotSeries; + + + +/***/ }, +/* 246 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var List = __webpack_require__(97); + var completeDimensions = __webpack_require__(102); + var WhiskerBoxDraw = __webpack_require__(247); + var zrUtil = __webpack_require__(4); + + function getItemValue(item) { + return item.value == null ? item : item.value; + } + + var seriesModelMixin = { + + /** + * @private + * @type {string} + */ + _baseAxisDim: null, + + /** + * @override + */ + getInitialData: function (option, ecModel) { + // When both types of xAxis and yAxis are 'value', layout is + // needed to be specified by user. Otherwise, layout can be + // judged by which axis is category. + + var categories; + + var xAxisModel = ecModel.getComponent('xAxis', this.get('xAxisIndex')); + var yAxisModel = ecModel.getComponent('yAxis', this.get('yAxisIndex')); + var xAxisType = xAxisModel.get('type'); + var yAxisType = yAxisModel.get('type'); + var addOrdinal; + + // FIXME + // 考虑时间轴 + + if (xAxisType === 'category') { + option.layout = 'horizontal'; + categories = xAxisModel.getCategories(); + addOrdinal = true; + } + else if (yAxisType === 'category') { + option.layout = 'vertical'; + categories = yAxisModel.getCategories(); + addOrdinal = true; + } + else { + option.layout = option.layout || 'horizontal'; + } + + this._baseAxisDim = option.layout === 'horizontal' ? 'x' : 'y'; + + var data = option.data; + var dimensions = this.dimensions = ['base'].concat(this.valueDimensions); + completeDimensions(dimensions, data); + + var list = new List(dimensions, this); + list.initData(data, categories ? categories.slice() : null, function (dataItem, dimName, idx, dimIdx) { + var value = getItemValue(dataItem); + return addOrdinal ? (dimName === 'base' ? idx : value[dimIdx - 1]) : value[dimIdx]; + }); + + return list; + }, + + /** + * Used by Gird. + * @param {string} axisDim 'x' or 'y' + * @return {Array.} dimensions on the axis. + */ + coordDimToDataDim: function (axisDim) { + var dims = this.valueDimensions.slice(); + var baseDim = ['base']; + var map = { + horizontal: {x: baseDim, y: dims}, + vertical: {x: dims, y: baseDim} + }; + return map[this.get('layout')][axisDim]; + }, + + /** + * @override + * @param {string|number} dataDim + * @return {string} coord dimension + */ + dataDimToCoordDim: function (dataDim) { + var dim; + + zrUtil.each(['x', 'y'], function (coordDim, index) { + var dataDims = this.coordDimToDataDim(coordDim); + if (zrUtil.indexOf(dataDims, dataDim) >= 0) { + dim = coordDim; + } + }, this); + + return dim; + }, + + /** + * If horizontal, base axis is x, otherwise y. + * @override + */ + getBaseAxis: function () { + var dim = this._baseAxisDim; + return this.ecModel.getComponent(dim + 'Axis', this.get(dim + 'AxisIndex')).axis; + } + }; + + var viewMixin = { + + init: function () { + /** + * Old data. + * @private + * @type {module:echarts/chart/helper/WhiskerBoxDraw} + */ + var whiskerBoxDraw = this._whiskerBoxDraw = new WhiskerBoxDraw( + this.getStyleUpdater() + ); + this.group.add(whiskerBoxDraw.group); + }, + + render: function (seriesModel, ecModel, api) { + this._whiskerBoxDraw.updateData(seriesModel.getData()); + }, + + remove: function (ecModel) { + this._whiskerBoxDraw.remove(); + } + }; + + module.exports = { + seriesModelMixin: seriesModelMixin, + viewMixin: viewMixin + }; + + +/***/ }, +/* 247 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Symbol + */ + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Path = __webpack_require__(45); + + var WhiskerPath = Path.extend({ + + type: 'whiskerInBox', + + shape: {}, + + buildPath: function (ctx, shape) { + for (var i in shape) { + if (i.indexOf('ends') === 0) { + var pts = shape[i]; + ctx.moveTo(pts[0][0], pts[0][1]); + ctx.lineTo(pts[1][0], pts[1][1]); + } + } + } + }); + + /** + * @constructor + * @alias {module:echarts/chart/helper/WhiskerBox} + * @param {module:echarts/data/List} data + * @param {number} idx + * @param {Function} styleUpdater + * @param {boolean} isInit + * @extends {module:zrender/graphic/Group} + */ + function WhiskerBox(data, idx, styleUpdater, isInit) { + graphic.Group.call(this); + + /** + * @type {number} + * @readOnly + */ + this.bodyIndex; + + /** + * @type {number} + * @readOnly + */ + this.whiskerIndex; + + /** + * @type {Function} + */ + this.styleUpdater = styleUpdater; + + this._createContent(data, idx, isInit); + + this.updateData(data, idx, isInit); + + /** + * Last series model. + * @type {module:echarts/model/Series} + */ + this._seriesModel; + } + + var whiskerBoxProto = WhiskerBox.prototype; + + whiskerBoxProto._createContent = function (data, idx, isInit) { + var itemLayout = data.getItemLayout(idx); + var constDim = itemLayout.chartLayout === 'horizontal' ? 1 : 0; + var count = 0; + + // Whisker element. + this.add(new graphic.Polygon({ + shape: { + points: isInit + ? transInit(itemLayout.bodyEnds, constDim, itemLayout) + : itemLayout.bodyEnds + }, + style: {strokeNoScale: true}, + z2: 100 + })); + this.bodyIndex = count++; + + // Box element. + var whiskerEnds = zrUtil.map(itemLayout.whiskerEnds, function (ends) { + return isInit ? transInit(ends, constDim, itemLayout) : ends; + }); + this.add(new WhiskerPath({ + shape: makeWhiskerEndsShape(whiskerEnds), + style: {strokeNoScale: true}, + z2: 100 + })); + this.whiskerIndex = count++; + }; + + function transInit(points, dim, itemLayout) { + return zrUtil.map(points, function (point) { + point = point.slice(); + point[dim] = itemLayout.initBaseline; + return point; + }); + } + + function makeWhiskerEndsShape(whiskerEnds) { + // zr animation only support 2-dim array. + var shape = {}; + zrUtil.each(whiskerEnds, function (ends, i) { + shape['ends' + i] = ends; + }); + return shape; + } + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + whiskerBoxProto.updateData = function (data, idx, isInit) { + var seriesModel = this._seriesModel = data.hostModel; + var itemLayout = data.getItemLayout(idx); + var updateMethod = graphic[isInit ? 'initProps' : 'updateProps']; + // this.childAt(this.bodyIndex).stopAnimation(true); + // this.childAt(this.whiskerIndex).stopAnimation(true); + updateMethod( + this.childAt(this.bodyIndex), + {shape: {points: itemLayout.bodyEnds}}, + seriesModel, idx + ); + updateMethod( + this.childAt(this.whiskerIndex), + {shape: makeWhiskerEndsShape(itemLayout.whiskerEnds)}, + seriesModel, idx + ); + + this.styleUpdater.call(null, this, data, idx); + }; + + zrUtil.inherits(WhiskerBox, graphic.Group); + + + /** + * @constructor + * @alias module:echarts/chart/helper/WhiskerBoxDraw + */ + function WhiskerBoxDraw(styleUpdater) { + this.group = new graphic.Group(); + this.styleUpdater = styleUpdater; + } + + var whiskerBoxDrawProto = WhiskerBoxDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + whiskerBoxDrawProto.updateData = function (data) { + var group = this.group; + var oldData = this._data; + var styleUpdater = this.styleUpdater; + + data.diff(oldData) + .add(function (newIdx) { + if (data.hasValue(newIdx)) { + var symbolEl = new WhiskerBox(data, newIdx, styleUpdater, true); + data.setItemGraphicEl(newIdx, symbolEl); + group.add(symbolEl); + } + }) + .update(function (newIdx, oldIdx) { + var symbolEl = oldData.getItemGraphicEl(oldIdx); + + // Empty data + if (!data.hasValue(newIdx)) { + group.remove(symbolEl); + return; + } + + if (!symbolEl) { + symbolEl = new WhiskerBox(data, newIdx, styleUpdater); + } + else { + symbolEl.updateData(data, newIdx); + } + + // Add back + group.add(symbolEl); + + data.setItemGraphicEl(newIdx, symbolEl); + }) + .remove(function (oldIdx) { + var el = oldData.getItemGraphicEl(oldIdx); + el && group.remove(el); + }) + .execute(); + + this._data = data; + }; + + /** + * Remove symbols. + * @param {module:echarts/data/List} data + */ + whiskerBoxDrawProto.remove = function () { + var group = this.group; + var data = this._data; + this._data = null; + data && data.eachItemGraphicEl(function (el) { + el && group.remove(el); + }); + }; + + module.exports = WhiskerBoxDraw; + + +/***/ }, +/* 248 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + var whiskerBoxCommon = __webpack_require__(246); + + var BoxplotView = ChartView.extend({ + + type: 'boxplot', + + getStyleUpdater: function () { + return updateStyle; + } + }); + + zrUtil.mixin(BoxplotView, whiskerBoxCommon.viewMixin, true); + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + + function updateStyle(itemGroup, data, idx) { + var itemModel = data.getItemModel(idx); + var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath); + var borderColor = data.getItemVisual(idx, 'color'); + + // Exclude borderColor. + var itemStyle = normalItemStyleModel.getItemStyle(['borderColor']); + + var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex); + whiskerEl.style.set(itemStyle); + whiskerEl.style.stroke = borderColor; + whiskerEl.dirty(); + + var bodyEl = itemGroup.childAt(itemGroup.bodyIndex); + bodyEl.style.set(itemStyle); + bodyEl.style.stroke = borderColor; + bodyEl.dirty(); + + var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + graphic.setHoverStyle(itemGroup, hoverStyle); + } + + module.exports = BoxplotView; + + + +/***/ }, +/* 249 */ +/***/ function(module, exports) { + + + + var borderColorQuery = ['itemStyle', 'normal', 'borderColor']; + + module.exports = function (ecModel, api) { + + var globalColors = ecModel.get('color'); + + ecModel.eachRawSeriesByType('boxplot', function (seriesModel) { + + var defaulColor = globalColors[seriesModel.seriesIndex % globalColors.length]; + var data = seriesModel.getData(); + + data.setVisual({ + legendSymbol: 'roundRect', + // Use name 'color' but not 'borderColor' for legend usage and + // visual coding from other component like dataRange. + color: seriesModel.get(borderColorQuery) || defaulColor + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + data.setItemVisual( + idx, + {color: itemModel.get(borderColorQuery, true)} + ); + }); + } + }); + + }; + + +/***/ }, +/* 250 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var each = zrUtil.each; + + module.exports = function (ecModel) { + + var groupResult = groupSeriesByAxis(ecModel); + + each(groupResult, function (groupItem) { + var seriesModels = groupItem.seriesModels; + + if (!seriesModels.length) { + return; + } + + calculateBase(groupItem); + + each(seriesModels, function (seriesModel, idx) { + layoutSingleSeries( + seriesModel, + groupItem.boxOffsetList[idx], + groupItem.boxWidthList[idx] + ); + }); + }); + }; + + /** + * Group series by axis. + */ + function groupSeriesByAxis(ecModel) { + var result = []; + var axisList = []; + + ecModel.eachSeriesByType('boxplot', function (seriesModel) { + var baseAxis = seriesModel.getBaseAxis(); + var idx = zrUtil.indexOf(axisList, baseAxis); + + if (idx < 0) { + idx = axisList.length; + axisList[idx] = baseAxis; + result[idx] = {axis: baseAxis, seriesModels: []}; + } + + result[idx].seriesModels.push(seriesModel); + }); + + return result; + } + + /** + * Calculate offset and box width for each series. + */ + function calculateBase(groupItem) { + var extent; + var baseAxis = groupItem.axis; + var seriesModels = groupItem.seriesModels; + var seriesCount = seriesModels.length; + + var boxWidthList = groupItem.boxWidthList = []; + var boxOffsetList = groupItem.boxOffsetList = []; + var boundList = []; + + var bandWidth; + if (baseAxis.type === 'category') { + bandWidth = baseAxis.getBandWidth(); + } + else { + var maxDataCount = 0; + each(seriesModels, function (seriesModel) { + maxDataCount = Math.max(maxDataCount, seriesModel.getData().count()); + }); + extent = baseAxis.getExtent(), + Math.abs(extent[1] - extent[0]) / maxDataCount; + } + + each(seriesModels, function (seriesModel) { + var boxWidthBound = seriesModel.get('boxWidth'); + if (!zrUtil.isArray(boxWidthBound)) { + boxWidthBound = [boxWidthBound, boxWidthBound]; + } + boundList.push([ + parsePercent(boxWidthBound[0], bandWidth) || 0, + parsePercent(boxWidthBound[1], bandWidth) || 0 + ]); + }); + + var availableWidth = bandWidth * 0.8 - 2; + var boxGap = availableWidth / seriesCount * 0.3; + var boxWidth = (availableWidth - boxGap * (seriesCount - 1)) / seriesCount; + var base = boxWidth / 2 - availableWidth / 2; + + each(seriesModels, function (seriesModel, idx) { + boxOffsetList.push(base); + base += boxGap + boxWidth; + + boxWidthList.push( + Math.min(Math.max(boxWidth, boundList[idx][0]), boundList[idx][1]) + ); + }); + } + + /** + * Calculate points location for each series. + */ + function layoutSingleSeries(seriesModel, offset, boxWidth) { + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var dimensions = seriesModel.dimensions; + var chartLayout = seriesModel.get('layout'); + var halfWidth = boxWidth / 2; + + data.each(dimensions, function () { + var args = arguments; + var dimLen = dimensions.length; + var axisDimVal = args[0]; + var idx = args[dimLen]; + var variableDim = chartLayout === 'horizontal' ? 0 : 1; + var constDim = 1 - variableDim; + + var median = getPoint(args[3]); + var end1 = getPoint(args[1]); + var end5 = getPoint(args[5]); + var whiskerEnds = [ + [end1, getPoint(args[2])], + [end5, getPoint(args[4])] + ]; + layEndLine(end1); + layEndLine(end5); + layEndLine(median); + + var bodyEnds = []; + addBodyEnd(whiskerEnds[0][1], 0); + addBodyEnd(whiskerEnds[1][1], 1); + + data.setItemLayout(idx, { + chartLayout: chartLayout, + initBaseline: median[constDim], + median: median, + bodyEnds: bodyEnds, + whiskerEnds: whiskerEnds + }); + + function getPoint(val) { + var p = []; + p[variableDim] = axisDimVal; + p[constDim] = val; + var point; + if (isNaN(axisDimVal) || isNaN(val)) { + point = [NaN, NaN]; + } + else { + point = coordSys.dataToPoint(p); + point[variableDim] += offset; + } + return point; + } + + function addBodyEnd(point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[variableDim] += halfWidth; + point2[variableDim] -= halfWidth; + start + ? bodyEnds.push(point1, point2) + : bodyEnds.push(point2, point1); + } + + function layEndLine(endCenter) { + var line = [endCenter.slice(), endCenter.slice()]; + line[0][variableDim] -= halfWidth; + line[1][variableDim] += halfWidth; + whiskerEnds.push(line); + } + }); + } + + + +/***/ }, +/* 251 */ +/***/ function(module, exports, __webpack_require__) { + + + + var echarts = __webpack_require__(1); + + __webpack_require__(252); + __webpack_require__(253); + + echarts.registerPreprocessor( + __webpack_require__(254) + ); + + echarts.registerVisual(__webpack_require__(255)); + echarts.registerLayout(__webpack_require__(256)); + + + +/***/ }, +/* 252 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var SeriesModel = __webpack_require__(28); + var whiskerBoxCommon = __webpack_require__(246); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + var addCommas = formatUtil.addCommas; + + var CandlestickSeries = SeriesModel.extend({ + + type: 'series.candlestick', + + dependencies: ['xAxis', 'yAxis', 'grid'], + + /** + * @readOnly + */ + valueDimensions: ['open', 'close', 'lowest', 'highest'], + + /** + * @type {Array.} + * @readOnly + */ + dimensions: null, + + /** + * @override + */ + defaultOption: { + zlevel: 0, // 一级层叠 + z: 2, // 二级层叠 + coordinateSystem: 'cartesian2d', + legendHoverLink: true, + + hoverAnimation: true, + + // xAxisIndex: 0, + // yAxisIndex: 0, + + layout: null, // 'horizontal' or 'vertical' + + itemStyle: { + normal: { + color: '#c23531', // 阳线 positive + color0: '#314656', // 阴线 negative '#c23531', '#314656' + borderWidth: 1, + // FIXME + // ec2中使用的是lineStyle.color 和 lineStyle.color0 + borderColor: '#c23531', + borderColor0: '#314656' + }, + emphasis: { + borderWidth: 2 + } + }, + + animationUpdate: false, + animationEasing: 'linear', + animationDuration: 300 + }, + + /** + * Get dimension for shadow in dataZoom + * @return {string} dimension name + */ + getShadowDim: function () { + return 'open'; + }, + + /** + * @override + */ + formatTooltip: function (dataIndex, mutipleSeries) { + // It rearly use mutiple candlestick series in one cartesian, + // so only consider one series in this default tooltip. + var valueHTMLArr = zrUtil.map(this.valueDimensions, function (dim) { + return dim + ': ' + addCommas(this._data.get(dim, dataIndex)); + }, this); + + return encodeHTML(this.name) + '
' + valueHTMLArr.join('
'); + }, + + brushSelector: function (itemLayout, selectors) { + return selectors.rect(itemLayout.brushRect); + } + + }); + + zrUtil.mixin(CandlestickSeries, whiskerBoxCommon.seriesModelMixin, true); + + module.exports = CandlestickSeries; + + + +/***/ }, +/* 253 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ChartView = __webpack_require__(42); + var graphic = __webpack_require__(43); + var whiskerBoxCommon = __webpack_require__(246); + + var CandlestickView = ChartView.extend({ + + type: 'candlestick', + + getStyleUpdater: function () { + return updateStyle; + } + + }); + + zrUtil.mixin(CandlestickView, whiskerBoxCommon.viewMixin, true); + + // Update common properties + var normalStyleAccessPath = ['itemStyle', 'normal']; + var emphasisStyleAccessPath = ['itemStyle', 'emphasis']; + + function updateStyle(itemGroup, data, idx) { + var itemModel = data.getItemModel(idx); + var normalItemStyleModel = itemModel.getModel(normalStyleAccessPath); + var color = data.getItemVisual(idx, 'color'); + var borderColor = data.getItemVisual(idx, 'borderColor') || color; + + // Color must be excluded. + // Because symbol provide setColor individually to set fill and stroke + var itemStyle = normalItemStyleModel.getItemStyle( + ['color', 'color0', 'borderColor', 'borderColor0'] + ); + + var whiskerEl = itemGroup.childAt(itemGroup.whiskerIndex); + whiskerEl.useStyle(itemStyle); + whiskerEl.style.stroke = borderColor; + + var bodyEl = itemGroup.childAt(itemGroup.bodyIndex); + bodyEl.useStyle(itemStyle); + bodyEl.style.fill = color; + bodyEl.style.stroke = borderColor; + + var hoverStyle = itemModel.getModel(emphasisStyleAccessPath).getItemStyle(); + graphic.setHoverStyle(itemGroup, hoverStyle); + } + + + module.exports = CandlestickView; + + + +/***/ }, +/* 254 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + if (!option || !zrUtil.isArray(option.series)) { + return; + } + + // Translate 'k' to 'candlestick'. + zrUtil.each(option.series, function (seriesItem) { + if (zrUtil.isObject(seriesItem) && seriesItem.type === 'k') { + seriesItem.type = 'candlestick'; + } + }); + }; + + + +/***/ }, +/* 255 */ +/***/ function(module, exports) { + + + + var positiveBorderColorQuery = ['itemStyle', 'normal', 'borderColor']; + var negativeBorderColorQuery = ['itemStyle', 'normal', 'borderColor0']; + var positiveColorQuery = ['itemStyle', 'normal', 'color']; + var negativeColorQuery = ['itemStyle', 'normal', 'color0']; + + module.exports = function (ecModel, api) { + + ecModel.eachRawSeriesByType('candlestick', function (seriesModel) { + + var data = seriesModel.getData(); + + data.setVisual({ + legendSymbol: 'roundRect' + }); + + // Only visible series has each data be visual encoded + if (!ecModel.isSeriesFiltered(seriesModel)) { + data.each(function (idx) { + var itemModel = data.getItemModel(idx); + var sign = data.getItemLayout(idx).sign; + + data.setItemVisual( + idx, + { + color: itemModel.get( + sign > 0 ? positiveColorQuery : negativeColorQuery + ), + borderColor: itemModel.get( + sign > 0 ? positiveBorderColorQuery : negativeBorderColorQuery + ) + } + ); + }); + } + }); + + }; + + +/***/ }, +/* 256 */ +/***/ function(module, exports) { + + + + var CANDLE_MIN_WIDTH = 2; + var CANDLE_MIN_NICE_WIDTH = 5; + var GPA_MIN = 4; + + module.exports = function (ecModel) { + + ecModel.eachSeriesByType('candlestick', function (seriesModel) { + + var coordSys = seriesModel.coordinateSystem; + var data = seriesModel.getData(); + var dimensions = seriesModel.dimensions; + var chartLayout = seriesModel.get('layout'); + + var candleWidth = calculateCandleWidth(seriesModel, data); + + data.each(dimensions, function () { + var args = arguments; + var dimLen = dimensions.length; + var axisDimVal = args[0]; + var idx = args[dimLen]; + var variableDim = chartLayout === 'horizontal' ? 0 : 1; + var constDim = 1 - variableDim; + + var openVal = args[1]; + var closeVal = args[2]; + var lowestVal = args[3]; + var highestVal = args[4]; + + var ocLow = Math.min(openVal, closeVal); + var ocHigh = Math.max(openVal, closeVal); + + var ocLowPoint = getPoint(ocLow); + var ocHighPoint = getPoint(ocHigh); + var lowestPoint = getPoint(lowestVal); + var highestPoint = getPoint(highestVal); + + var whiskerEnds = [ + [highestPoint, ocHighPoint], + [lowestPoint, ocLowPoint] + ]; + + var bodyEnds = []; + addBodyEnd(ocHighPoint, 0); + addBodyEnd(ocLowPoint, 1); + + data.setItemLayout(idx, { + chartLayout: chartLayout, + sign: openVal > closeVal ? -1 : openVal < closeVal ? 1 : 0, + initBaseline: openVal > closeVal + ? ocHighPoint[constDim] : ocLowPoint[constDim], // open point. + bodyEnds: bodyEnds, + whiskerEnds: whiskerEnds, + brushRect: makeBrushRect() + }); + + function getPoint(val) { + var p = []; + p[variableDim] = axisDimVal; + p[constDim] = val; + return (isNaN(axisDimVal) || isNaN(val)) + ? [NaN, NaN] + : coordSys.dataToPoint(p); + } + + function addBodyEnd(point, start) { + var point1 = point.slice(); + var point2 = point.slice(); + point1[variableDim] += candleWidth / 2; + point2[variableDim] -= candleWidth / 2; + start + ? bodyEnds.push(point1, point2) + : bodyEnds.push(point2, point1); + } + + function makeBrushRect() { + var pmin = getPoint(Math.min(openVal, closeVal, lowestVal, highestVal)); + var pmax = getPoint(Math.max(openVal, closeVal, lowestVal, highestVal)); + + pmin[variableDim] -= candleWidth / 2; + pmax[variableDim] -= candleWidth / 2; + + return { + x: pmin[0], + y: pmin[1], + width: constDim ? candleWidth : pmax[0] - pmin[0], + height: constDim ? pmax[1] - pmin[1] : candleWidth + }; + } + + }, true); + }); + }; + + function calculateCandleWidth(seriesModel, data) { + var baseAxis = seriesModel.getBaseAxis(); + var extent; + + var bandWidth = baseAxis.type === 'category' + ? baseAxis.getBandWidth() + : ( + extent = baseAxis.getExtent(), + Math.abs(extent[1] - extent[0]) / data.count() + ); + + // Half band width is perfect when space is enouph, otherwise + // try not to be smaller than CANDLE_MIN_NICE_WIDTH (and only + // gap is compressed), otherwise ensure not to be smaller than + // CANDLE_MIN_WIDTH in spite of overlap. + + return bandWidth / 2 - 2 > CANDLE_MIN_NICE_WIDTH // "- 2" is minus border width + ? bandWidth / 2 - 2 + : bandWidth - CANDLE_MIN_NICE_WIDTH > GPA_MIN + ? CANDLE_MIN_NICE_WIDTH + : Math.max(bandWidth - GPA_MIN, CANDLE_MIN_WIDTH); + } + + + +/***/ }, +/* 257 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var echarts = __webpack_require__(1); + + __webpack_require__(258); + __webpack_require__(259); + + echarts.registerVisual(zrUtil.curry( + __webpack_require__(109), 'effectScatter', 'circle', null + )); + echarts.registerLayout(zrUtil.curry( + __webpack_require__(110), 'effectScatter' + )); + + +/***/ }, +/* 258 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var createListFromArray = __webpack_require__(101); + var SeriesModel = __webpack_require__(28); + + module.exports = SeriesModel.extend({ + + type: 'series.effectScatter', + + dependencies: ['grid', 'polar'], + + getInitialData: function (option, ecModel) { + var list = createListFromArray(option.data, this, ecModel); + return list; + }, + + brushSelector: 'point', + + defaultOption: { + coordinateSystem: 'cartesian2d', + zlevel: 0, + z: 2, + legendHoverLink: true, + + effectType: 'ripple', + + // When to show the effect, option: 'render'|'emphasis' + showEffectOn: 'render', + + // Ripple effect config + rippleEffect: { + period: 4, + // Scale of ripple + scale: 2.5, + // Brush type can be fill or stroke + brushType: 'fill' + }, + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Polar coordinate system + // polarIndex: 0, + + // Geo coordinate system + // geoIndex: 0, + + // symbol: null, // 图形类型 + symbolSize: 10 // 图形大小,半宽(半径)参数,当图形为方向或菱形则总宽度为symbolSize * 2 + // symbolRotate: null, // 图形旋转控制 + + // large: false, + // Available when large is true + // largeThreshold: 2000, + + // itemStyle: { + // normal: { + // opacity: 1 + // } + // } + } + + }); + + +/***/ }, +/* 259 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var EffectSymbol = __webpack_require__(260); + + __webpack_require__(1).extendChartView({ + + type: 'effectScatter', + + init: function () { + this._symbolDraw = new SymbolDraw(EffectSymbol); + }, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var effectSymbolDraw = this._symbolDraw; + effectSymbolDraw.updateData(data); + this.group.add(effectSymbolDraw.group); + }, + + updateLayout: function () { + this._symbolDraw.updateLayout(); + }, + + remove: function (ecModel, api) { + this._symbolDraw && this._symbolDraw.remove(api); + } + }); + + +/***/ }, +/* 260 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Symbol with ripple effect + * @module echarts/chart/helper/EffectSymbol + */ + + + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var graphic = __webpack_require__(43); + var numberUtil = __webpack_require__(7); + var Symbol = __webpack_require__(105); + var Group = graphic.Group; + + var EFFECT_RIPPLE_NUMBER = 3; + + function normalizeSymbolSize(symbolSize) { + if (!zrUtil.isArray(symbolSize)) { + symbolSize = [+symbolSize, +symbolSize]; + } + return symbolSize; + } + + function updateRipplePath(rippleGroup, effectCfg) { + rippleGroup.eachChild(function (ripplePath) { + ripplePath.attr({ + z: effectCfg.z, + zlevel: effectCfg.zlevel, + style: { + stroke: effectCfg.brushType === 'stroke' ? effectCfg.color : null, + fill: effectCfg.brushType === 'fill' ? effectCfg.color : null + } + }); + }); + } + /** + * @constructor + * @param {module:echarts/data/List} data + * @param {number} idx + * @extends {module:zrender/graphic/Group} + */ + function EffectSymbol(data, idx) { + Group.call(this); + + var symbol = new Symbol(data, idx); + var rippleGroup = new Group(); + this.add(symbol); + this.add(rippleGroup); + + rippleGroup.beforeUpdate = function () { + this.attr(symbol.getScale()); + }; + this.updateData(data, idx); + } + + var effectSymbolProto = EffectSymbol.prototype; + + effectSymbolProto.stopEffectAnimation = function () { + this.childAt(1).removeAll(); + }; + + effectSymbolProto.startEffectAnimation = function (effectCfg) { + var symbolType = effectCfg.symbolType; + var color = effectCfg.color; + var rippleGroup = this.childAt(1); + + for (var i = 0; i < EFFECT_RIPPLE_NUMBER; i++) { + var ripplePath = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + ripplePath.attr({ + style: { + strokeNoScale: true + }, + z2: 99, + silent: true, + scale: [1, 1] + }); + + var delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset; + // TODO Configurable effectCfg.period + ripplePath.animate('', true) + .when(effectCfg.period, { + scale: [effectCfg.rippleScale, effectCfg.rippleScale] + }) + .delay(delay) + .start(); + ripplePath.animateStyle(true) + .when(effectCfg.period, { + opacity: 0 + }) + .delay(delay) + .start(); + + rippleGroup.add(ripplePath); + } + + updateRipplePath(rippleGroup, effectCfg); + }; + + /** + * Update effect symbol + */ + effectSymbolProto.updateEffectAnimation = function (effectCfg) { + var oldEffectCfg = this._effectCfg; + var rippleGroup = this.childAt(1); + + // Must reinitialize effect if following configuration changed + var DIFFICULT_PROPS = ['symbolType', 'period', 'rippleScale']; + for (var i = 0; i < DIFFICULT_PROPS; i++) { + var propName = DIFFICULT_PROPS[i]; + if (oldEffectCfg[propName] !== effectCfg[propName]) { + this.stopEffectAnimation(); + this.startEffectAnimation(effectCfg); + return; + } + } + + updateRipplePath(rippleGroup, effectCfg); + }; + + /** + * Highlight symbol + */ + effectSymbolProto.highlight = function () { + this.trigger('emphasis'); + }; + + /** + * Downplay symbol + */ + effectSymbolProto.downplay = function () { + this.trigger('normal'); + }; + + /** + * Update symbol properties + * @param {module:echarts/data/List} data + * @param {number} idx + */ + effectSymbolProto.updateData = function (data, idx) { + var seriesModel = data.hostModel; + + this.childAt(0).updateData(data, idx); + + var rippleGroup = this.childAt(1); + var itemModel = data.getItemModel(idx); + var symbolType = data.getItemVisual(idx, 'symbol'); + var symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); + var color = data.getItemVisual(idx, 'color'); + + rippleGroup.attr('scale', symbolSize); + + rippleGroup.traverse(function (ripplePath) { + ripplePath.attr({ + fill: color + }); + }); + + var symbolOffset = itemModel.getShallow('symbolOffset'); + if (symbolOffset) { + var pos = rippleGroup.position; + pos[0] = numberUtil.parsePercent(symbolOffset[0], symbolSize[0]); + pos[1] = numberUtil.parsePercent(symbolOffset[1], symbolSize[1]); + } + rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0; + + var effectCfg = {}; + + effectCfg.showEffectOn = seriesModel.get('showEffectOn'); + effectCfg.rippleScale = itemModel.get('rippleEffect.scale'); + effectCfg.brushType = itemModel.get('rippleEffect.brushType'); + effectCfg.period = itemModel.get('rippleEffect.period') * 1000; + effectCfg.effectOffset = idx / data.count(); + effectCfg.z = itemModel.getShallow('z') || 0; + effectCfg.zlevel = itemModel.getShallow('zlevel') || 0; + effectCfg.symbolType = symbolType; + effectCfg.color = color; + + this.off('mouseover').off('mouseout').off('emphasis').off('normal'); + + if (effectCfg.showEffectOn === 'render') { + this._effectCfg + ? this.updateEffectAnimation(effectCfg) + : this.startEffectAnimation(effectCfg); + + this._effectCfg = effectCfg; + } + else { + // Not keep old effect config + this._effectCfg = null; + + this.stopEffectAnimation(); + var symbol = this.childAt(0); + var onEmphasis = function () { + symbol.trigger('emphasis'); + if (effectCfg.showEffectOn !== 'render') { + this.startEffectAnimation(effectCfg); + } + }; + var onNormal = function () { + symbol.trigger('normal'); + if (effectCfg.showEffectOn !== 'render') { + this.stopEffectAnimation(); + } + }; + this.on('mouseover', onEmphasis, this) + .on('mouseout', onNormal, this) + .on('emphasis', onEmphasis, this) + .on('normal', onNormal, this); + } + + this._effectCfg = effectCfg; + }; + + effectSymbolProto.fadeOut = function (cb) { + this.off('mouseover').off('mouseout').off('emphasis').off('normal'); + cb && cb(); + }; + + zrUtil.inherits(EffectSymbol, Group); + + module.exports = EffectSymbol; + + +/***/ }, +/* 261 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(262); + __webpack_require__(263); + + var echarts = __webpack_require__(1); + echarts.registerLayout( + __webpack_require__(268) + ); + + +/***/ }, +/* 262 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var SeriesModel = __webpack_require__(28); + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var CoordinateSystem = __webpack_require__(26); + + // Convert [ [{coord: []}, {coord: []}] ] + // to [ { coords: [[]] } ] + function preprocessOption (seriesOpt) { + var data = seriesOpt.data; + if (data && data[0] && data[0][0] && data[0][0].coord) { + if (true) { + console.warn('Lines data configuration has been changed to' + + ' { coords:[[1,2],[2,3]] }'); + } + seriesOpt.data = zrUtil.map(data, function (itemOpt) { + var coords = [ + itemOpt[0].coord, itemOpt[1].coord + ]; + var target = { + coords: coords + }; + if (itemOpt[0].name) { + target.fromName = itemOpt[0].name; + } + if (itemOpt[1].name) { + target.toName = itemOpt[1].name; + } + return zrUtil.mergeAll([target, itemOpt[0], itemOpt[1]]); + }); + } + } + + var LinesSeries = SeriesModel.extend({ + + type: 'series.lines', + + dependencies: ['grid', 'polar'], + + visualColorAccessPath: 'lineStyle.normal.color', + + init: function (option) { + // Not using preprocessor because mergeOption may not have series.type + preprocessOption(option); + + LinesSeries.superApply(this, 'init', arguments); + }, + + mergeOption: function (option) { + preprocessOption(option); + + LinesSeries.superApply(this, 'mergeOption', arguments); + }, + + getInitialData: function (option, ecModel) { + if (true) { + var CoordSys = CoordinateSystem.get(option.coordinateSystem); + if (!CoordSys) { + throw new Error('Unkown coordinate system ' + option.coordinateSystem); + } + } + + var lineData = new List(['value'], this); + lineData.hasItemOption = false; + lineData.initData(option.data, [], function (dataItem, dimName, dataIndex, dimIndex) { + // dataItem is simply coords + if (dataItem instanceof Array) { + return NaN; + } + else { + lineData.hasItemOption = true; + var value = dataItem.value; + if (value) { + return value instanceof Array ? value[dimIndex] : value; + } + } + }); + + return lineData; + }, + + formatTooltip: function (dataIndex) { + var data = this.getData(); + var itemModel = data.getItemModel(dataIndex); + var name = itemModel.get('name'); + if (name) { + return name; + } + var fromName = itemModel.get('fromName'); + var toName = itemModel.get('toName'); + return fromName + ' > ' + toName; + }, + + defaultOption: { + coordinateSystem: 'geo', + zlevel: 0, + z: 2, + legendHoverLink: true, + + hoverAnimation: true, + // Cartesian coordinate system + xAxisIndex: 0, + yAxisIndex: 0, + + // Geo coordinate system + geoIndex: 0, + + effect: { + show: false, + period: 4, + // Animation delay. support callback + // delay: 0, + // If move with constant speed px/sec + // period will be ignored if this property is > 0, + constantSpeed: 0, + symbol: 'circle', + symbolSize: 3, + loop: true, + // Length of trail, 0 - 1 + trailLength: 0.2 + // Same with lineStyle.normal.color + // color + }, + + large: false, + // Available when large is true + largeThreshold: 2000, + + // If lines are polyline + // polyline not support curveness, label, animation + polyline: false, + + label: { + normal: { + show: false, + position: 'end' + // distance: 5, + // formatter: 标签文本格式器,同Tooltip.formatter,不支持异步回调 + } + }, + + lineStyle: { + normal: { + opacity: 0.5 + } + } + } + }); + + +/***/ }, +/* 263 */ +/***/ function(module, exports, __webpack_require__) { + + + + var LineDraw = __webpack_require__(199); + var EffectLine = __webpack_require__(264); + var Line = __webpack_require__(200); + var Polyline = __webpack_require__(265); + var EffectPolyline = __webpack_require__(266); + var LargeLineDraw = __webpack_require__(267); + + __webpack_require__(1).extendChartView({ + + type: 'lines', + + init: function () {}, + + render: function (seriesModel, ecModel, api) { + var data = seriesModel.getData(); + var lineDraw = this._lineDraw; + + var hasEffect = seriesModel.get('effect.show'); + var isPolyline = seriesModel.get('polyline'); + var isLarge = seriesModel.get('large') && data.count() >= seriesModel.get('largeThreshold'); + + if (true) { + if (hasEffect && isLarge) { + console.warn('Large lines not support effect'); + } + } + if (hasEffect !== this._hasEffet || isPolyline !== this._isPolyline || isLarge !== this._isLarge) { + if (lineDraw) { + lineDraw.remove(); + } + lineDraw = this._lineDraw = isLarge + ? new LargeLineDraw() + : new LineDraw( + isPolyline + ? (hasEffect ? EffectPolyline : Polyline) + : (hasEffect ? EffectLine : Line) + ); + this._hasEffet = hasEffect; + this._isPolyline = isPolyline; + this._isLarge = isLarge; + } + + var zlevel = seriesModel.get('zlevel'); + var trailLength = seriesModel.get('effect.trailLength'); + + var zr = api.getZr(); + // Avoid the drag cause ghost shadow + // FIXME Better way ? + zr.painter.getLayer(zlevel).clear(true); + // Config layer with motion blur + if (this._lastZlevel != null) { + zr.configLayer(this._lastZlevel, { + motionBlur: false + }); + } + if (hasEffect && trailLength) { + if (true) { + var notInIndividual = false; + ecModel.eachSeries(function (otherSeriesModel) { + if (otherSeriesModel !== seriesModel && otherSeriesModel.get('zlevel') === zlevel) { + notInIndividual = true; + } + }); + notInIndividual && console.warn('Lines with trail effect should have an individual zlevel'); + } + + zr.configLayer(zlevel, { + motionBlur: true, + lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) + }); + } + + this.group.add(lineDraw.group); + + lineDraw.updateData(data); + + this._lastZlevel = zlevel; + }, + + updateLayout: function (seriesModel, ecModel, api) { + this._lineDraw.updateLayout(seriesModel); + // Not use motion when dragging or zooming + var zr = api.getZr(); + zr.painter.getLayer(this._lastZlevel).clear(true); + }, + + remove: function (ecModel, api) { + this._lineDraw && this._lineDraw.remove(api, true); + } + }); + + +/***/ }, +/* 264 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + + + var graphic = __webpack_require__(43); + var Line = __webpack_require__(200); + var zrUtil = __webpack_require__(4); + var symbolUtil = __webpack_require__(106); + var vec2 = __webpack_require__(10); + + var curveUtil = __webpack_require__(50); + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Line} + */ + function EffectLine(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this.add(this.createLine(lineData, idx, seriesScope)); + + this._updateEffectSymbol(lineData, idx); + } + + var effectLineProto = EffectLine.prototype; + + effectLineProto.createLine = function (lineData, idx, seriesScope) { + return new Line(lineData, idx, seriesScope); + }; + + effectLineProto._updateEffectSymbol = function (lineData, idx) { + var itemModel = lineData.getItemModel(idx); + var effectModel = itemModel.getModel('effect'); + var size = effectModel.get('symbolSize'); + var symbolType = effectModel.get('symbol'); + if (!zrUtil.isArray(size)) { + size = [size, size]; + } + var color = effectModel.get('color') || lineData.getItemVisual(idx, 'color'); + var symbol = this.childAt(1); + + if (this._symbolType !== symbolType) { + // Remove previous + this.remove(symbol); + + symbol = symbolUtil.createSymbol( + symbolType, -0.5, -0.5, 1, 1, color + ); + symbol.z2 = 100; + symbol.culling = true; + + this.add(symbol); + } + + // Symbol may be removed if loop is false + if (!symbol) { + return; + } + + // Shadow color is same with color in default + symbol.setStyle('shadowColor', color); + symbol.setStyle(effectModel.getItemStyle(['color'])); + + symbol.attr('scale', size); + + symbol.setColor(color); + symbol.attr('scale', size); + + this._symbolType = symbolType; + + this._updateEffectAnimation(lineData, effectModel, idx); + }; + + effectLineProto._updateEffectAnimation = function (lineData, effectModel, idx) { + + var symbol = this.childAt(1); + if (!symbol) { + return; + } + + var self = this; + + var points = lineData.getItemLayout(idx); + + var period = effectModel.get('period') * 1000; + var loop = effectModel.get('loop'); + var constantSpeed = effectModel.get('constantSpeed'); + var delayExpr = effectModel.get('delay') || function (idx) { + return idx / lineData.count() * period / 3; + }; + var isDelayFunc = typeof delayExpr === 'function'; + + // Ignore when updating + symbol.ignore = true; + + this.updateAnimationPoints(symbol, points); + + if (constantSpeed > 0) { + period = this.getLineLength(symbol) / constantSpeed * 1000; + } + + if (period !== this._period || loop !== this._loop) { + + symbol.stopAnimation(); + + var delay = delayExpr; + if (isDelayFunc) { + delay = delayExpr(idx); + } + if (symbol.__t > 0) { + delay = -period * symbol.__t; + } + symbol.__t = 0; + var animator = symbol.animate('', loop) + .when(period, { + __t: 1 + }) + .delay(delay) + .during(function () { + self.updateSymbolPosition(symbol); + }); + if (!loop) { + animator.done(function () { + self.remove(symbol); + }); + } + animator.start(); + } + + this._period = period; + this._loop = loop; + }; + + effectLineProto.getLineLength = function (symbol) { + // Not so accurate + return (vec2.dist(symbol.__p1, symbol.__cp1) + + vec2.dist(symbol.__cp1, symbol.__p2)); + }; + + effectLineProto.updateAnimationPoints = function (symbol, points) { + symbol.__p1 = points[0]; + symbol.__p2 = points[1]; + symbol.__cp1 = points[2] || [ + (points[0][0] + points[1][0]) / 2, + (points[0][1] + points[1][1]) / 2 + ]; + }; + + effectLineProto.updateData = function (lineData, idx, seriesScope) { + this.childAt(0).updateData(lineData, idx, seriesScope); + this._updateEffectSymbol(lineData, idx); + }; + + effectLineProto.updateSymbolPosition = function (symbol) { + var p1 = symbol.__p1; + var p2 = symbol.__p2; + var cp1 = symbol.__cp1; + var t = symbol.__t; + var pos = symbol.position; + var quadraticAt = curveUtil.quadraticAt; + var quadraticDerivativeAt = curveUtil.quadraticDerivativeAt; + pos[0] = quadraticAt(p1[0], cp1[0], p2[0], t); + pos[1] = quadraticAt(p1[1], cp1[1], p2[1], t); + + // Tangent + var tx = quadraticDerivativeAt(p1[0], cp1[0], p2[0], t); + var ty = quadraticDerivativeAt(p1[1], cp1[1], p2[1], t); + + symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2; + + symbol.ignore = false; + }; + + + effectLineProto.updateLayout = function (lineData, idx) { + this.childAt(0).updateLayout(lineData, idx); + + var effectModel = lineData.getItemModel(idx).getModel('effect'); + this._updateEffectAnimation(lineData, effectModel, idx); + }; + + zrUtil.inherits(EffectLine, graphic.Group); + + module.exports = EffectLine; + + +/***/ }, +/* 265 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/chart/helper/Line + */ + + + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + + /** + * @constructor + * @extends {module:zrender/graphic/Group} + * @alias {module:echarts/chart/helper/Polyline} + */ + function Polyline(lineData, idx, seriesScope) { + graphic.Group.call(this); + + this._createPolyline(lineData, idx, seriesScope); + } + + var polylineProto = Polyline.prototype; + + polylineProto._createPolyline = function (lineData, idx, seriesScope) { + // var seriesModel = lineData.hostModel; + var points = lineData.getItemLayout(idx); + + var line = new graphic.Polyline({ + shape: { + points: points + } + }); + + this.add(line); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + polylineProto.updateData = function (lineData, idx, seriesScope) { + var seriesModel = lineData.hostModel; + + var line = this.childAt(0); + var target = { + shape: { + points: lineData.getItemLayout(idx) + } + }; + graphic.updateProps(line, target, seriesModel, idx); + + this._updateCommonStl(lineData, idx, seriesScope); + }; + + polylineProto._updateCommonStl = function (lineData, idx, seriesScope) { + var line = this.childAt(0); + var itemModel = lineData.getItemModel(idx); + + var visualColor = lineData.getItemVisual(idx, 'color'); + + var lineStyle = seriesScope && seriesScope.lineStyle; + var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle; + + if (!seriesScope || lineData.hasItemOption) { + lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle(); + hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle(); + } + line.useStyle(zrUtil.defaults( + { + strokeNoScale: true, + fill: 'none', + stroke: visualColor + }, + lineStyle + )); + line.hoverStyle = hoverLineStyle; + + graphic.setHoverStyle(this); + }; + + polylineProto.updateLayout = function (lineData, idx) { + var polyline = this.childAt(0); + polyline.setShape('points', lineData.getItemLayout(idx)); + }; + + zrUtil.inherits(Polyline, graphic.Group); + + module.exports = Polyline; + + +/***/ }, +/* 266 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Provide effect for line + * @module echarts/chart/helper/EffectLine + */ + + + var Polyline = __webpack_require__(265); + var zrUtil = __webpack_require__(4); + var EffectLine = __webpack_require__(264); + var vec2 = __webpack_require__(10); + + /** + * @constructor + * @extends {module:echarts/chart/helper/EffectLine} + * @alias {module:echarts/chart/helper/Polyline} + */ + function EffectPolyline(lineData, idx, seriesScope) { + EffectLine.call(this, lineData, idx, seriesScope); + this._lastFrame = 0; + this._lastFramePercent = 0; + } + + var effectPolylineProto = EffectPolyline.prototype; + + // Overwrite + effectPolylineProto.createLine = function (lineData, idx, seriesScope) { + return new Polyline(lineData, idx, seriesScope); + }; + + // Overwrite + effectPolylineProto.updateAnimationPoints = function (symbol, points) { + this._points = points; + var accLenArr = [0]; + var len = 0; + for (var i = 1; i < points.length; i++) { + var p1 = points[i - 1]; + var p2 = points[i]; + len += vec2.dist(p1, p2); + accLenArr.push(len); + } + if (len === 0) { + return; + } + + for (var i = 0; i < accLenArr.length; i++) { + accLenArr[i] /= len; + } + this._offsets = accLenArr; + this._length = len; + }; + + // Overwrite + effectPolylineProto.getLineLength = function (symbol) { + return this._length; + }; + + // Overwrite + effectPolylineProto.updateSymbolPosition = function (symbol) { + var t = symbol.__t; + var points = this._points; + var offsets = this._offsets; + var len = points.length; + + if (!offsets) { + // Has length 0 + return; + } + + var lastFrame = this._lastFrame; + var frame; + + if (t < this._lastFramePercent) { + // Start from the next frame + // PENDING start from lastFrame ? + var start = Math.min(lastFrame + 1, len - 1); + for (frame = start; frame >= 0; frame--) { + if (offsets[frame] <= t) { + break; + } + } + // PENDING really need to do this ? + frame = Math.min(frame, len - 2); + } + else { + for (var frame = lastFrame; frame < len; frame++) { + if (offsets[frame] > t) { + break; + } + } + frame = Math.min(frame - 1, len - 2); + } + + vec2.lerp( + symbol.position, points[frame], points[frame + 1], + (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]) + ); + + this._lastFrame = frame; + this._lastFramePercent = t; + + symbol.ignore = false; + }; + + zrUtil.inherits(EffectPolyline, EffectLine); + + module.exports = EffectPolyline; + + +/***/ }, +/* 267 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Batch by color + + + + var graphic = __webpack_require__(43); + + var quadraticContain = __webpack_require__(55); + var lineContain = __webpack_require__(53); + + var LargeLineShape = graphic.extendShape({ + shape: { + polyline: false, + + segs: [] + }, + + buildPath: function (path, shape) { + var segs = shape.segs; + var isPolyline = shape.polyline; + + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (isPolyline) { + path.moveTo(seg[0][0], seg[0][1]); + for (var j = 1; j < seg.length; j++) { + path.lineTo(seg[j][0], seg[j][1]); + } + } + else { + path.moveTo(seg[0][0], seg[0][1]); + if (seg.length > 2) { + path.quadraticCurveTo(seg[2][0], seg[2][1], seg[1][0], seg[1][1]); + } + else { + path.lineTo(seg[1][0], seg[1][1]); + } + } + } + }, + + findDataIndex: function (x, y) { + var shape = this.shape; + var segs = shape.segs; + var isPolyline = shape.polyline; + var lineWidth = Math.max(this.style.lineWidth, 1); + + // Not consider transform + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (isPolyline) { + for (var j = 1; j < seg.length; j++) { + if (lineContain.containStroke( + seg[j - 1][0], seg[j - 1][1], seg[j][0], seg[j][1], lineWidth, x, y + )) { + return i; + } + } + } + else { + if (seg.length > 2) { + if (quadraticContain.containStroke( + seg[0][0], seg[0][1], seg[2][0], seg[2][1], seg[1][0], seg[1][1], lineWidth, x, y + )) { + return i; + } + } + else { + if (lineContain.containStroke( + seg[0][0], seg[0][1], seg[1][0], seg[1][1], lineWidth, x, y + )) { + return i; + } + } + } + } + + return -1; + } + }); + + function LargeLineDraw() { + this.group = new graphic.Group(); + + this._lineEl = new LargeLineShape(); + } + + var largeLineProto = LargeLineDraw.prototype; + + /** + * Update symbols draw by new data + * @param {module:echarts/data/List} data + */ + largeLineProto.updateData = function (data) { + this.group.removeAll(); + + var lineEl = this._lineEl; + + var seriesModel = data.hostModel; + + lineEl.setShape({ + segs: data.mapArray(data.getItemLayout), + polyline: seriesModel.get('polyline') + }); + + lineEl.useStyle( + seriesModel.getModel('lineStyle.normal').getLineStyle() + ); + + var visualColor = data.getVisual('color'); + if (visualColor) { + lineEl.setStyle('stroke', visualColor); + } + lineEl.setStyle('fill'); + + // Enable tooltip + // PENDING May have performance issue when path is extremely large + lineEl.seriesIndex = seriesModel.seriesIndex; + lineEl.on('mousemove', function (e) { + lineEl.dataIndex = null; + var dataIndex = lineEl.findDataIndex(e.offsetX, e.offsetY); + if (dataIndex > 0) { + // Provide dataIndex for tooltip + lineEl.dataIndex = dataIndex; + } + }); + + // Add back + this.group.add(lineEl); + }; + + largeLineProto.updateLayout = function (seriesModel) { + var data = seriesModel.getData(); + this._lineEl.setShape({ + segs: data.mapArray(data.getItemLayout) + }); + }; + + largeLineProto.remove = function () { + this.group.removeAll(); + }; + + module.exports = LargeLineDraw; + + +/***/ }, +/* 268 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = function (ecModel) { + ecModel.eachSeriesByType('lines', function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var lineData = seriesModel.getData(); + + // FIXME Use data dimensions ? + lineData.each(function (idx) { + var itemModel = lineData.getItemModel(idx); + // TODO Support pure array + var coords = (itemModel.option instanceof Array) ? + itemModel.option : itemModel.get('coords'); + + if (true) { + if (!(coords instanceof Array && coords.length > 0 && coords[0] instanceof Array)) { + throw new Error('Invalid coords ' + JSON.stringify(coords) + '. Lines must have 2d coords array in data item.'); + } + } + var pts = []; + + if (seriesModel.get('polyline')) { + for (var i = 0; i < coords.length; i++) { + pts.push(coordSys.dataToPoint(coords[i])); + } + } + else { + pts[0] = coordSys.dataToPoint(coords[0]); + pts[1] = coordSys.dataToPoint(coords[1]); + + var curveness = itemModel.get('lineStyle.normal.curveness'); + if (+curveness) { + pts[2] = [ + (pts[0][0] + pts[1][0]) / 2 - (pts[0][1] - pts[1][1]) * curveness, + (pts[0][1] + pts[1][1]) / 2 - (pts[1][0] - pts[0][0]) * curveness + ]; + } + } + lineData.setItemLayout(idx, pts); + }); + }); + }; + + +/***/ }, +/* 269 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(270); + __webpack_require__(271); + + +/***/ }, +/* 270 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SeriesModel = __webpack_require__(28); + var createListFromArray = __webpack_require__(101); + + module.exports = SeriesModel.extend({ + type: 'series.heatmap', + + getInitialData: function (option, ecModel) { + return createListFromArray(option.data, this, ecModel); + }, + + defaultOption: { + + // Cartesian2D or geo + coordinateSystem: 'cartesian2d', + + zlevel: 0, + + z: 2, + + // Cartesian coordinate system + // xAxisIndex: 0, + // yAxisIndex: 0, + + // Geo coordinate system + geoIndex: 0, + + blurSize: 30, + + pointSize: 20, + + maxOpacity: 1, + + minOpacity: 0 + } + }); + + +/***/ }, +/* 271 */ +/***/ function(module, exports, __webpack_require__) { + + + + var graphic = __webpack_require__(43); + var HeatmapLayer = __webpack_require__(272); + var zrUtil = __webpack_require__(4); + + function getIsInPiecewiseRange(dataExtent, pieceList, selected) { + var dataSpan = dataExtent[1] - dataExtent[0]; + pieceList = zrUtil.map(pieceList, function (piece) { + return { + interval: [ + (piece.interval[0] - dataExtent[0]) / dataSpan, + (piece.interval[1] - dataExtent[0]) / dataSpan + ] + }; + }); + var len = pieceList.length; + var lastIndex = 0; + return function (val) { + // Try to find in the location of the last found + for (var i = lastIndex; i < len; i++) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + if (i === len) { // Not found, back interation + for (var i = lastIndex - 1; i >= 0; i--) { + var interval = pieceList[i].interval; + if (interval[0] <= val && val <= interval[1]) { + lastIndex = i; + break; + } + } + } + return i >= 0 && i < len && selected[i]; + }; + } + + function getIsInContinuousRange(dataExtent, range) { + var dataSpan = dataExtent[1] - dataExtent[0]; + range = [ + (range[0] - dataExtent[0]) / dataSpan, + (range[1] - dataExtent[0]) / dataSpan + ]; + return function (val) { + return val >= range[0] && val <= range[1]; + }; + } + + function isGeoCoordSys(coordSys) { + var dimensions = coordSys.dimensions; + // Not use coorSys.type === 'geo' because coordSys maybe extended + return dimensions[0] === 'lng' && dimensions[1] === 'lat'; + } + + module.exports = __webpack_require__(1).extendChartView({ + + type: 'heatmap', + + render: function (seriesModel, ecModel, api) { + var visualMapOfThisSeries; + ecModel.eachComponent('visualMap', function (visualMap) { + visualMap.eachTargetSeries(function (targetSeries) { + if (targetSeries === seriesModel) { + visualMapOfThisSeries = visualMap; + } + }); + }); + + if (true) { + if (!visualMapOfThisSeries) { + throw new Error('Heatmap must use with visualMap'); + } + } + + this.group.removeAll(); + var coordSys = seriesModel.coordinateSystem; + if (coordSys.type === 'cartesian2d') { + this._renderOnCartesian(coordSys, seriesModel, api); + } + else if (isGeoCoordSys(coordSys)) { + this._renderOnGeo( + coordSys, seriesModel, visualMapOfThisSeries, api + ); + } + }, + + _renderOnCartesian: function (cartesian, seriesModel, api) { + var xAxis = cartesian.getAxis('x'); + var yAxis = cartesian.getAxis('y'); + var group = this.group; + + if (true) { + if (!(xAxis.type === 'category' && yAxis.type === 'category')) { + throw new Error('Heatmap on cartesian must have two category axes'); + } + if (!(xAxis.onBand && yAxis.onBand)) { + throw new Error('Heatmap on cartesian must have two axes with boundaryGap true'); + } + } + + var width = xAxis.getBandWidth(); + var height = yAxis.getBandWidth(); + + var data = seriesModel.getData(); + + var itemStyleQuery = 'itemStyle.normal'; + var hoverItemStyleQuery = 'itemStyle.emphasis'; + var labelQuery = 'label.normal'; + var hoverLabelQuery = 'label.emphasis'; + var style = seriesModel.getModel(itemStyleQuery).getItemStyle(['color']); + var hoverStl = seriesModel.getModel(hoverItemStyleQuery).getItemStyle(); + var labelModel = seriesModel.getModel('label.normal'); + var hoverLabelModel = seriesModel.getModel('label.emphasis'); + + data.each(['x', 'y', 'z'], function (x, y, z, idx) { + var itemModel = data.getItemModel(idx); + var point = cartesian.dataToPoint([x, y]); + // Ignore empty data + if (isNaN(z)) { + return; + } + var rect = new graphic.Rect({ + shape: { + x: point[0] - width / 2, + y: point[1] - height / 2, + width: width, + height: height + }, + style: { + fill: data.getItemVisual(idx, 'color'), + opacity: data.getItemVisual(idx, 'opacity') + } + }); + // Optimization for large datset + if (data.hasItemOption) { + style = itemModel.getModel(itemStyleQuery).getItemStyle(['color']); + hoverStl = itemModel.getModel(hoverItemStyleQuery).getItemStyle(); + labelModel = itemModel.getModel(labelQuery); + hoverLabelModel = itemModel.getModel(hoverLabelQuery); + } + + var rawValue = seriesModel.getRawValue(idx); + var defaultText = '-'; + if (rawValue && rawValue[2] != null) { + defaultText = rawValue[2]; + } + if (labelModel.getShallow('show')) { + graphic.setText(style, labelModel); + style.text = seriesModel.getFormattedLabel(idx, 'normal') || defaultText; + } + if (hoverLabelModel.getShallow('show')) { + graphic.setText(hoverStl, hoverLabelModel); + hoverStl.text = seriesModel.getFormattedLabel(idx, 'emphasis') || defaultText; + } + + rect.setStyle(style); + + graphic.setHoverStyle(rect, data.hasItemOption ? hoverStl : zrUtil.extend({}, hoverStl)); + + group.add(rect); + data.setItemGraphicEl(idx, rect); + }); + }, + + _renderOnGeo: function (geo, seriesModel, visualMapModel, api) { + var inRangeVisuals = visualMapModel.targetVisuals.inRange; + var outOfRangeVisuals = visualMapModel.targetVisuals.outOfRange; + // if (!visualMapping) { + // throw new Error('Data range must have color visuals'); + // } + + var data = seriesModel.getData(); + var hmLayer = this._hmLayer || (this._hmLayer || new HeatmapLayer()); + hmLayer.blurSize = seriesModel.get('blurSize'); + hmLayer.pointSize = seriesModel.get('pointSize'); + hmLayer.minOpacity = seriesModel.get('minOpacity'); + hmLayer.maxOpacity = seriesModel.get('maxOpacity'); + + var rect = geo.getViewRect().clone(); + var roamTransform = geo.getRoamTransform().transform; + rect.applyTransform(roamTransform); + + // Clamp on viewport + var x = Math.max(rect.x, 0); + var y = Math.max(rect.y, 0); + var x2 = Math.min(rect.width + rect.x, api.getWidth()); + var y2 = Math.min(rect.height + rect.y, api.getHeight()); + var width = x2 - x; + var height = y2 - y; + + var points = data.mapArray(['lng', 'lat', 'value'], function (lng, lat, value) { + var pt = geo.dataToPoint([lng, lat]); + pt[0] -= x; + pt[1] -= y; + pt.push(value); + return pt; + }); + + var dataExtent = visualMapModel.getExtent(); + var isInRange = visualMapModel.type === 'visualMap.continuous' + ? getIsInContinuousRange(dataExtent, visualMapModel.option.range) + : getIsInPiecewiseRange( + dataExtent, visualMapModel.getPieceList(), visualMapModel.option.selected + ); + + hmLayer.update( + points, width, height, + inRangeVisuals.color.getNormalizer(), + { + inRange: inRangeVisuals.color.getColorMapper(), + outOfRange: outOfRangeVisuals.color.getColorMapper() + }, + isInRange + ); + var img = new graphic.Image({ + style: { + width: width, + height: height, + x: x, + y: y, + image: hmLayer.canvas + }, + silent: true + }); + this.group.add(img); + } + }); + + +/***/ }, +/* 272 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file defines echarts Heatmap Chart + * @author Ovilia (me@zhangwenli.com) + * Inspired by https://github.com/mourner/simpleheat + * + * @module + */ + + + var GRADIENT_LEVELS = 256; + var zrUtil = __webpack_require__(4); + + /** + * Heatmap Chart + * + * @class + */ + function Heatmap() { + var canvas = zrUtil.createCanvas(); + this.canvas = canvas; + + this.blurSize = 30; + this.pointSize = 20; + + this.maxOpacity = 1; + this.minOpacity = 0; + + this._gradientPixels = {}; + } + + Heatmap.prototype = { + /** + * Renders Heatmap and returns the rendered canvas + * @param {Array} data array of data, each has x, y, value + * @param {number} width canvas width + * @param {number} height canvas height + */ + update: function(data, width, height, normalize, colorFunc, isInRange) { + var brush = this._getBrush(); + var gradientInRange = this._getGradient(data, colorFunc, 'inRange'); + var gradientOutOfRange = this._getGradient(data, colorFunc, 'outOfRange'); + var r = this.pointSize + this.blurSize; + + var canvas = this.canvas; + var ctx = canvas.getContext('2d'); + var len = data.length; + canvas.width = width; + canvas.height = height; + for (var i = 0; i < len; ++i) { + var p = data[i]; + var x = p[0]; + var y = p[1]; + var value = p[2]; + + // calculate alpha using value + var alpha = normalize(value); + + // draw with the circle brush with alpha + ctx.globalAlpha = alpha; + ctx.drawImage(brush, x - r, y - r); + } + + // colorize the canvas using alpha value and set with gradient + var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + var pixels = imageData.data; + var offset = 0; + var pixelLen = pixels.length; + var minOpacity = this.minOpacity; + var maxOpacity = this.maxOpacity; + var diffOpacity = maxOpacity - minOpacity; + + while(offset < pixelLen) { + var alpha = pixels[offset + 3] / 256; + var gradientOffset = Math.floor(alpha * (GRADIENT_LEVELS - 1)) * 4; + // Simple optimize to ignore the empty data + if (alpha > 0) { + var gradient = isInRange(alpha) ? gradientInRange : gradientOutOfRange; + // Any alpha > 0 will be mapped to [minOpacity, maxOpacity] + alpha > 0 && (alpha = alpha * diffOpacity + minOpacity); + pixels[offset++] = gradient[gradientOffset]; + pixels[offset++] = gradient[gradientOffset + 1]; + pixels[offset++] = gradient[gradientOffset + 2]; + pixels[offset++] = gradient[gradientOffset + 3] * alpha * 256; + } + else { + offset += 4; + } + } + ctx.putImageData(imageData, 0, 0); + + return canvas; + }, + + /** + * get canvas of a black circle brush used for canvas to draw later + * @private + * @returns {Object} circle brush canvas + */ + _getBrush: function() { + var brushCanvas = this._brushCanvas || (this._brushCanvas = zrUtil.createCanvas()); + // set brush size + var r = this.pointSize + this.blurSize; + var d = r * 2; + brushCanvas.width = d; + brushCanvas.height = d; + + var ctx = brushCanvas.getContext('2d'); + ctx.clearRect(0, 0, d, d); + + // in order to render shadow without the distinct circle, + // draw the distinct circle in an invisible place, + // and use shadowOffset to draw shadow in the center of the canvas + ctx.shadowOffsetX = d; + ctx.shadowBlur = this.blurSize; + // draw the shadow in black, and use alpha and shadow blur to generate + // color in color map + ctx.shadowColor = '#000'; + + // draw circle in the left to the canvas + ctx.beginPath(); + ctx.arc(-r, r, this.pointSize, 0, Math.PI * 2, true); + ctx.closePath(); + ctx.fill(); + return brushCanvas; + }, + + /** + * get gradient color map + * @private + */ + _getGradient: function (data, colorFunc, state) { + var gradientPixels = this._gradientPixels; + var pixelsSingleState = gradientPixels[state] || (gradientPixels[state] = new Uint8ClampedArray(256 * 4)); + var color = []; + var off = 0; + for (var i = 0; i < 256; i++) { + colorFunc[state](i / 255, true, color); + pixelsSingleState[off++] = color[0]; + pixelsSingleState[off++] = color[1]; + pixelsSingleState[off++] = color[2]; + pixelsSingleState[off++] = color[3]; + } + return pixelsSingleState; + } + }; + + module.exports = Heatmap; + + + +/***/ }, +/* 273 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Legend component entry file8 + */ + + + __webpack_require__(274); + __webpack_require__(275); + __webpack_require__(276); + + var echarts = __webpack_require__(1); + // Series Filter + echarts.registerProcessor(__webpack_require__(278)); + + +/***/ }, +/* 274 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Model = __webpack_require__(12); + + var LegendModel = __webpack_require__(1).extendComponentModel({ + + type: 'legend', + + dependencies: ['series'], + + layoutMode: { + type: 'box', + ignoreSize: true + }, + + init: function (option, parentModel, ecModel) { + this.mergeDefaultAndTheme(option, ecModel); + + option.selected = option.selected || {}; + }, + + mergeOption: function (option) { + LegendModel.superCall(this, 'mergeOption', option); + }, + + optionUpdated: function () { + this._updateData(this.ecModel); + + var legendData = this._data; + + // If selectedMode is single, try to select one + if (legendData[0] && this.get('selectedMode') === 'single') { + var hasSelected = false; + // If has any selected in option.selected + for (var i = 0; i < legendData.length; i++) { + var name = legendData[i].get('name'); + if (this.isSelected(name)) { + // Force to unselect others + this.select(name); + hasSelected = true; + break; + } + } + // Try select the first if selectedMode is single + !hasSelected && this.select(legendData[0].get('name')); + } + }, + + _updateData: function (ecModel) { + var legendData = zrUtil.map(this.get('data') || [], function (dataItem) { + // Can be string or number + if (typeof dataItem === 'string' || typeof dataItem === 'number') { + dataItem = { + name: dataItem + }; + } + return new Model(dataItem, this, this.ecModel); + }, this); + this._data = legendData; + + var availableNames = zrUtil.map(ecModel.getSeries(), function (series) { + return series.name; + }); + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.legendDataProvider) { + var data = seriesModel.legendDataProvider(); + availableNames = availableNames.concat(data.mapArray(data.getName)); + } + }); + /** + * @type {Array.} + * @private + */ + this._availableNames = availableNames; + }, + + /** + * @return {Array.} + */ + getData: function () { + return this._data; + }, + + /** + * @param {string} name + */ + select: function (name) { + var selected = this.option.selected; + var selectedMode = this.get('selectedMode'); + if (selectedMode === 'single') { + var data = this._data; + zrUtil.each(data, function (dataItem) { + selected[dataItem.get('name')] = false; + }); + } + selected[name] = true; + }, + + /** + * @param {string} name + */ + unSelect: function (name) { + if (this.get('selectedMode') !== 'single') { + this.option.selected[name] = false; + } + }, + + /** + * @param {string} name + */ + toggleSelected: function (name) { + var selected = this.option.selected; + // Default is true + if (!(name in selected)) { + selected[name] = true; + } + this[selected[name] ? 'unSelect' : 'select'](name); + }, + + /** + * @param {string} name + */ + isSelected: function (name) { + var selected = this.option.selected; + return !((name in selected) && !selected[name]) + && zrUtil.indexOf(this._availableNames, name) >= 0; + }, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 4, + show: true, + + // 布局方式,默认为水平布局,可选为: + // 'horizontal' | 'vertical' + orient: 'horizontal', + + left: 'center', + // right: 'center', + + top: 'top', + // bottom: 'top', + + // 水平对齐 + // 'auto' | 'left' | 'right' + // 默认为 'auto', 根据 x 的位置判断是左对齐还是右对齐 + align: 'auto', + + backgroundColor: 'rgba(0,0,0,0)', + // 图例边框颜色 + borderColor: '#ccc', + // 图例边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + // 图例内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + // 各个item之间的间隔,单位px,默认为10, + // 横向布局时为水平间隔,纵向布局时为纵向间隔 + itemGap: 10, + // 图例图形宽度 + itemWidth: 25, + // 图例图形高度 + itemHeight: 14, + + // 图例关闭时候的颜色 + inactiveColor: '#ccc', + + textStyle: { + // 图例文字颜色 + color: '#333' + }, + // formatter: '', + // 选择模式,默认开启图例开关 + selectedMode: true, + // 配置默认选中状态,可配合LEGEND.SELECTED事件做动态数据载入 + // selected: null, + // 图例内容(详见legend.data,数组中每一项代表一个item + // data: [], + + // Tooltip 相关配置 + tooltip: { + show: false + } + } + }); + + module.exports = LegendModel; + + +/***/ }, +/* 275 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Legend action + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + function legendSelectActionHandler(methodName, payload, ecModel) { + var selectedMap = {}; + var isToggleSelect = methodName === 'toggleSelected'; + var isSelected; + // Update all legend components + ecModel.eachComponent('legend', function (legendModel) { + if (isToggleSelect && isSelected != null) { + // Force other legend has same selected status + // Or the first is toggled to true and other are toggled to false + // In the case one legend has some item unSelected in option. And if other legend + // doesn't has the item, they will assume it is selected. + legendModel[isSelected ? 'select' : 'unSelect'](payload.name); + } + else { + legendModel[methodName](payload.name); + isSelected = legendModel.isSelected(payload.name); + } + var legendData = legendModel.getData(); + zrUtil.each(legendData, function (model) { + var name = model.get('name'); + // Wrap element + if (name === '\n' || name === '') { + return; + } + var isItemSelected = legendModel.isSelected(name); + if (name in selectedMap) { + // Unselected if any legend is unselected + selectedMap[name] = selectedMap[name] && isItemSelected; + } + else { + selectedMap[name] = isItemSelected; + } + }); + }); + // Return the event explicitly + return { + name: payload.name, + selected: selectedMap + }; + } + /** + * @event legendToggleSelect + * @type {Object} + * @property {string} type 'legendToggleSelect' + * @property {string} [from] + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendToggleSelect', 'legendselectchanged', + zrUtil.curry(legendSelectActionHandler, 'toggleSelected') + ); + + /** + * @event legendSelect + * @type {Object} + * @property {string} type 'legendSelect' + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendSelect', 'legendselected', + zrUtil.curry(legendSelectActionHandler, 'select') + ); + + /** + * @event legendUnSelect + * @type {Object} + * @property {string} type 'legendUnSelect' + * @property {string} name Series name or data item name + */ + echarts.registerAction( + 'legendUnSelect', 'legendunselected', + zrUtil.curry(legendSelectActionHandler, 'unSelect') + ); + + +/***/ }, +/* 276 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var symbolCreator = __webpack_require__(106); + var graphic = __webpack_require__(43); + var listComponentHelper = __webpack_require__(277); + + var curry = zrUtil.curry; + + function dispatchSelectAction(name, api) { + api.dispatchAction({ + type: 'legendToggleSelect', + name: name + }); + } + + function dispatchHighlightAction(seriesModel, dataName, api) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + seriesModel.get('legendHoverLink') && api.dispatchAction({ + type: 'highlight', + seriesName: seriesModel.name, + name: dataName + }); + } + } + + function dispatchDownplayAction(seriesModel, dataName, api) { + // If element hover will move to a hoverLayer. + var el = api.getZr().storage.getDisplayList()[0]; + if (!(el && el.useHoverLayer)) { + seriesModel.get('legendHoverLink') && api.dispatchAction({ + type: 'downplay', + seriesName: seriesModel.name, + name: dataName + }); + } + } + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'legend', + + init: function () { + this._symbolTypeStore = {}; + }, + + render: function (legendModel, ecModel, api) { + var group = this.group; + group.removeAll(); + + if (!legendModel.get('show')) { + return; + } + + var selectMode = legendModel.get('selectedMode'); + var itemAlign = legendModel.get('align'); + + if (itemAlign === 'auto') { + itemAlign = (legendModel.get('left') === 'right' + && legendModel.get('orient') === 'vertical') + ? 'right' : 'left'; + } + + var legendDrawedMap = {}; + + zrUtil.each(legendModel.getData(), function (itemModel) { + var name = itemModel.get('name'); + + // Use empty string or \n as a newline string + if (name === '' || name === '\n') { + group.add(new graphic.Group({ + newline: true + })); + return; + } + + var seriesModel = ecModel.getSeriesByName(name)[0]; + + if (legendDrawedMap[name]) { + // Have been drawed + return; + } + + // Series legend + if (seriesModel) { + var data = seriesModel.getData(); + var color = data.getVisual('color'); + + // If color is a callback function + if (typeof color === 'function') { + // Use the first data + color = color(seriesModel.getDataParams(0)); + } + + // Using rect symbol defaultly + var legendSymbolType = data.getVisual('legendSymbol') || 'roundRect'; + var symbolType = data.getVisual('symbol'); + + var itemGroup = this._createItem( + name, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, + selectMode + ); + + itemGroup.on('click', curry(dispatchSelectAction, name, api)) + .on('mouseover', curry(dispatchHighlightAction, seriesModel, '', api)) + .on('mouseout', curry(dispatchDownplayAction, seriesModel, '', api)); + + legendDrawedMap[name] = true; + } + else { + // Data legend of pie, funnel + ecModel.eachRawSeries(function (seriesModel) { + // In case multiple series has same data name + if (legendDrawedMap[name]) { + return; + } + if (seriesModel.legendDataProvider) { + var data = seriesModel.legendDataProvider(); + var idx = data.indexOfName(name); + if (idx < 0) { + return; + } + + var color = data.getItemVisual(idx, 'color'); + + var legendSymbolType = 'roundRect'; + + var itemGroup = this._createItem( + name, itemModel, legendModel, + legendSymbolType, null, + itemAlign, color, + selectMode + ); + + itemGroup.on('click', curry(dispatchSelectAction, name, api)) + // FIXME Should not specify the series name + .on('mouseover', curry(dispatchHighlightAction, seriesModel, name, api)) + .on('mouseout', curry(dispatchDownplayAction, seriesModel, name, api)); + + legendDrawedMap[name] = true; + } + }, this); + } + + if (true) { + if (!legendDrawedMap[name]) { + console.warn(name + ' series not exists. Legend data should be same with series name or data name.'); + } + } + }, this); + + listComponentHelper.layout(group, legendModel, api); + // Render background after group is layout + // FIXME + listComponentHelper.addBackground(group, legendModel); + }, + + _createItem: function ( + name, itemModel, legendModel, + legendSymbolType, symbolType, + itemAlign, color, selectMode + ) { + var itemWidth = legendModel.get('itemWidth'); + var itemHeight = legendModel.get('itemHeight'); + var inactiveColor = legendModel.get('inactiveColor'); + + var isSelected = legendModel.isSelected(name); + var itemGroup = new graphic.Group(); + + var textStyleModel = itemModel.getModel('textStyle'); + + var itemIcon = itemModel.get('icon'); + + var tooltipModel = itemModel.getModel('tooltip'); + + // Use user given icon first + legendSymbolType = itemIcon || legendSymbolType; + itemGroup.add(symbolCreator.createSymbol( + legendSymbolType, 0, 0, itemWidth, itemHeight, isSelected ? color : inactiveColor + )); + + // Compose symbols + // PENDING + if (!itemIcon && symbolType + // At least show one symbol, can't be all none + && ((symbolType !== legendSymbolType) || symbolType == 'none') + ) { + var size = itemHeight * 0.8; + if (symbolType === 'none') { + symbolType = 'circle'; + } + // Put symbol in the center + itemGroup.add(symbolCreator.createSymbol( + symbolType, (itemWidth - size) / 2, (itemHeight - size) / 2, size, size, + isSelected ? color : inactiveColor + )); + } + + // Text + var textX = itemAlign === 'left' ? itemWidth + 5 : -5; + var textAlign = itemAlign; + + var formatter = legendModel.get('formatter'); + var content = name; + if (typeof formatter === 'string' && formatter) { + content = formatter.replace('{name}', name); + } + else if (typeof formatter === 'function') { + content = formatter(name); + } + + var text = new graphic.Text({ + style: { + text: content, + x: textX, + y: itemHeight / 2, + fill: isSelected ? textStyleModel.getTextColor() : inactiveColor, + textFont: textStyleModel.getFont(), + textAlign: textAlign, + textVerticalAlign: 'middle' + } + }); + itemGroup.add(text); + + // Add a invisible rect to increase the area of mouse hover + var hitRect = new graphic.Rect({ + shape: itemGroup.getBoundingRect(), + invisible: true, + tooltip: tooltipModel.get('show') ? zrUtil.extend({ + content: name, + // Defaul formatter + formatter: function () { + return name; + }, + formatterParams: { + componentType: 'legend', + legendIndex: legendModel.componentIndex, + name: name, + $vars: ['name'] + } + }, tooltipModel.option) : null + }); + itemGroup.add(hitRect); + + itemGroup.eachChild(function (child) { + child.silent = true; + }); + + hitRect.silent = !selectMode; + + + + this.group.add(itemGroup); + + graphic.setHoverStyle(itemGroup); + + return itemGroup; + } + }); + + +/***/ }, +/* 277 */ +/***/ function(module, exports, __webpack_require__) { + + + // List layout + var layout = __webpack_require__(21); + var formatUtil = __webpack_require__(6); + var graphic = __webpack_require__(43); + + function positionGroup(group, model, api) { + layout.positionGroup( + group, model.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + }, + model.get('padding') + ); + } + + module.exports = { + /** + * Layout list like component. + * It will box layout each items in group of component and then position the whole group in the viewport + * @param {module:zrender/group/Group} group + * @param {module:echarts/model/Component} componentModel + * @param {module:echarts/ExtensionAPI} + */ + layout: function (group, componentModel, api) { + var rect = layout.getLayoutRect(componentModel.getBoxLayoutParams(), { + width: api.getWidth(), + height: api.getHeight() + }, componentModel.get('padding')); + layout.box( + componentModel.get('orient'), + group, + componentModel.get('itemGap'), + rect.width, + rect.height + ); + + positionGroup(group, componentModel, api); + }, + + addBackground: function (group, componentModel) { + var padding = formatUtil.normalizeCssArray( + componentModel.get('padding') + ); + var boundingRect = group.getBoundingRect(); + var style = componentModel.getItemStyle(['color', 'opacity']); + style.fill = componentModel.get('backgroundColor'); + var rect = new graphic.Rect({ + shape: { + x: boundingRect.x - padding[3], + y: boundingRect.y - padding[0], + width: boundingRect.width + padding[1] + padding[3], + height: boundingRect.height + padding[0] + padding[2] + }, + style: style, + silent: true, + z2: -1 + }); + graphic.subPixelOptimizeRect(rect); + + group.add(rect); + } + }; + + +/***/ }, +/* 278 */ +/***/ function(module, exports) { + + + module.exports = function (ecModel) { + var legendModels = ecModel.findComponents({ + mainType: 'legend' + }); + if (legendModels && legendModels.length) { + ecModel.filterSeries(function (series) { + // If in any legend component the status is not selected. + // Because in legend series is assumed selected when it is not in the legend data. + for (var i = 0; i < legendModels.length; i++) { + if (!legendModels[i].isSelected(series.name)) { + return false; + } + } + return true; + }); + } + }; + + +/***/ }, +/* 279 */ +/***/ function(module, exports, __webpack_require__) { + + // FIXME Better way to pack data in graphic element + + + __webpack_require__(280); + + __webpack_require__(281); + + // Show tip action + /** + * @action + * @property {string} type + * @property {number} seriesIndex + * @property {number} dataIndex + * @property {number} [x] + * @property {number} [y] + */ + __webpack_require__(1).registerAction( + { + type: 'showTip', + event: 'showTip', + update: 'none' + }, + // noop + function () {} + ); + // Hide tip action + __webpack_require__(1).registerAction( + { + type: 'hideTip', + event: 'hideTip', + update: 'none' + }, + // noop + function () {} + ); + + +/***/ }, +/* 280 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(1).extendComponentModel({ + + type: 'tooltip', + + defaultOption: { + zlevel: 0, + + z: 8, + + show: true, + + // tooltip主体内容 + showContent: true, + + // 触发类型,默认数据触发,见下图,可选为:'item' ¦ 'axis' + trigger: 'item', + + // 触发条件,支持 'click' | 'mousemove' + triggerOn: 'mousemove', + + // 是否永远显示 content + alwaysShowContent: false, + + // 位置 {Array} | {Function} + // position: null + + // 内容格式器:{string}(Template) ¦ {Function} + // formatter: null + + showDelay: 0, + + // 隐藏延迟,单位ms + hideDelay: 100, + + // 动画变换时间,单位s + transitionDuration: 0.4, + + enterable: false, + + // 提示背景颜色,默认为透明度为0.7的黑色 + backgroundColor: 'rgba(50,50,50,0.7)', + + // 提示边框颜色 + borderColor: '#333', + + // 提示边框圆角,单位px,默认为4 + borderRadius: 4, + + // 提示边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + + // 提示内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + + // Extra css text + extraCssText: '', + + // 坐标轴指示器,坐标轴触发有效 + axisPointer: { + // 默认为直线 + // 可选为:'line' | 'shadow' | 'cross' + type: 'line', + + // type 为 line 的时候有效,指定 tooltip line 所在的轴,可选 + // 可选 'x' | 'y' | 'angle' | 'radius' | 'auto' + // 默认 'auto',会选择类型为 cateogry 的轴,对于双数值轴,笛卡尔坐标系会默认选择 x 轴 + // 极坐标系会默认选择 angle 轴 + axis: 'auto', + + animation: true, + animationDurationUpdate: 200, + animationEasingUpdate: 'exponentialOut', + + // 直线指示器样式设置 + lineStyle: { + color: '#555', + width: 1, + type: 'solid' + }, + + crossStyle: { + color: '#555', + width: 1, + type: 'dashed', + + // TODO formatter + textStyle: {} + }, + + // 阴影指示器样式设置 + shadowStyle: { + color: 'rgba(150,150,150,0.3)' + } + }, + textStyle: { + color: '#fff', + fontSize: 14 + } + } + }); + + +/***/ }, +/* 281 */ +/***/ function(module, exports, __webpack_require__) { + + + + var TooltipContent = __webpack_require__(282); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var formatUtil = __webpack_require__(6); + var numberUtil = __webpack_require__(7); + var parsePercent = numberUtil.parsePercent; + var env = __webpack_require__(2); + var Model = __webpack_require__(12); + + function dataEqual(a, b) { + if (!a || !b) { + return false; + } + var round = numberUtil.round; + return round(a[0]) === round(b[0]) + && round(a[1]) === round(b[1]); + } + /** + * @inner + */ + function makeLineShape(x1, y1, x2, y2) { + return { + x1: x1, + y1: y1, + x2: x2, + y2: y2 + }; + } + + /** + * @inner + */ + function makeRectShape(x, y, width, height) { + return { + x: x, + y: y, + width: width, + height: height + }; + } + + /** + * @inner + */ + function makeSectorShape(cx, cy, r0, r, startAngle, endAngle) { + return { + cx: cx, + cy: cy, + r0: r0, + r: r, + startAngle: startAngle, + endAngle: endAngle, + clockwise: true + }; + } + + function refixTooltipPosition(x, y, el, viewWidth, viewHeight) { + var width = el.clientWidth; + var height = el.clientHeight; + var gap = 20; + + if (x + width + gap > viewWidth) { + x -= width + gap; + } + else { + x += gap; + } + if (y + height + gap > viewHeight) { + y -= height + gap; + } + else { + y += gap; + } + return [x, y]; + } + + function calcTooltipPosition(position, rect, dom) { + var domWidth = dom.clientWidth; + var domHeight = dom.clientHeight; + var gap = 5; + var x = 0; + var y = 0; + var rectWidth = rect.width; + var rectHeight = rect.height; + switch (position) { + case 'inside': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'top': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y - domHeight - gap; + break; + case 'bottom': + x = rect.x + rectWidth / 2 - domWidth / 2; + y = rect.y + rectHeight + gap; + break; + case 'left': + x = rect.x - domWidth - gap; + y = rect.y + rectHeight / 2 - domHeight / 2; + break; + case 'right': + x = rect.x + rectWidth + gap; + y = rect.y + rectHeight / 2 - domHeight / 2; + } + return [x, y]; + } + + /** + * @param {string|Function|Array.} positionExpr + * @param {number} x Mouse x + * @param {number} y Mouse y + * @param {module:echarts/component/tooltip/TooltipContent} content + * @param {Object|} params + * @param {module:zrender/Element} el target element + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ + function updatePosition(positionExpr, x, y, content, params, el, api) { + var viewWidth = api.getWidth(); + var viewHeight = api.getHeight(); + + var rect = el && el.getBoundingRect().clone(); + el && rect.applyTransform(el.transform); + if (typeof positionExpr === 'function') { + // Callback of position can be an array or a string specify the position + positionExpr = positionExpr([x, y], params, content.el, rect); + } + + if (zrUtil.isArray(positionExpr)) { + x = parsePercent(positionExpr[0], viewWidth); + y = parsePercent(positionExpr[1], viewHeight); + } + // Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element + else if (typeof positionExpr === 'string' && el) { + var pos = calcTooltipPosition( + positionExpr, rect, content.el + ); + x = pos[0]; + y = pos[1]; + } + else { + var pos = refixTooltipPosition( + x, y, content.el, viewWidth, viewHeight + ); + x = pos[0]; + y = pos[1]; + } + + content.moveTo(x, y); + } + + function ifSeriesSupportAxisTrigger(seriesModel) { + var coordSys = seriesModel.coordinateSystem; + var trigger = seriesModel.get('tooltip.trigger', true); + // Ignore series use item tooltip trigger and series coordinate system is not cartesian or + return !(!coordSys + || (coordSys.type !== 'cartesian2d' && coordSys.type !== 'polar' && coordSys.type !== 'singleAxis') + || trigger === 'item'); + } + + __webpack_require__(1).extendComponentView({ + + type: 'tooltip', + + _axisPointers: {}, + + init: function (ecModel, api) { + if (env.node) { + return; + } + var tooltipContent = new TooltipContent(api.getDom(), api); + this._tooltipContent = tooltipContent; + + api.on('showTip', this._manuallyShowTip, this); + api.on('hideTip', this._manuallyHideTip, this); + }, + + render: function (tooltipModel, ecModel, api) { + if (env.node) { + return; + } + + // Reset + this.group.removeAll(); + + /** + * @type {Object} + * @private + */ + this._axisPointers = {}; + + /** + * @private + * @type {module:echarts/component/tooltip/TooltipModel} + */ + this._tooltipModel = tooltipModel; + + /** + * @private + * @type {module:echarts/model/Global} + */ + this._ecModel = ecModel; + + /** + * @private + * @type {module:echarts/ExtensionAPI} + */ + this._api = api; + + /** + * @type {Object} + * @private + */ + this._lastHover = { + // data + // payloadBatch + }; + + var tooltipContent = this._tooltipContent; + tooltipContent.update(); + tooltipContent.enterable = tooltipModel.get('enterable'); + this._alwaysShowContent = tooltipModel.get('alwaysShowContent'); + + /** + * @type {Object.} + */ + this._seriesGroupByAxis = this._prepareAxisTriggerData( + tooltipModel, ecModel + ); + + var crossText = this._crossText; + if (crossText) { + this.group.add(crossText); + } + + // Try to keep the tooltip show when refreshing + if (this._lastX != null && this._lastY != null) { + var self = this; + clearTimeout(this._refreshUpdateTimeout); + this._refreshUpdateTimeout = setTimeout(function () { + // Show tip next tick after other charts are rendered + // In case highlight action has wrong result + // FIXME + self._manuallyShowTip({ + x: self._lastX, + y: self._lastY + }); + }); + } + + var zr = this._api.getZr(); + zr.off('click', this._tryShow); + zr.off('mousemove', this._mousemove); + zr.off('mouseout', this._hide); + zr.off('globalout', this._hide); + if (tooltipModel.get('triggerOn') === 'click') { + zr.on('click', this._tryShow, this); + } + else { + zr.on('mousemove', this._mousemove, this); + zr.on('mouseout', this._hide, this); + zr.on('globalout', this._hide, this); + } + }, + + _mousemove: function (e) { + var showDelay = this._tooltipModel.get('showDelay'); + var self = this; + clearTimeout(this._showTimeout); + if (showDelay > 0) { + this._showTimeout = setTimeout(function () { + self._tryShow(e); + }, showDelay); + } + else { + this._tryShow(e); + } + }, + + /** + * Show tip manually by + * dispatchAction({ + * type: 'showTip', + * x: 10, + * y: 10 + * }); + * Or + * dispatchAction({ + * type: 'showTip', + * seriesIndex: 0, + * dataIndex: 1 + * }); + * + * TODO Batch + */ + _manuallyShowTip: function (event) { + // From self + if (event.from === this.uid) { + return; + } + + var ecModel = this._ecModel; + var seriesIndex = event.seriesIndex; + var dataIndex = event.dataIndex; + var seriesModel = ecModel.getSeriesByIndex(seriesIndex); + var api = this._api; + + if (event.x == null || event.y == null) { + if (!seriesModel) { + // Find the first series can use axis trigger + ecModel.eachSeries(function (_series) { + if (ifSeriesSupportAxisTrigger(_series) && !seriesModel) { + seriesModel = _series; + } + }); + } + if (seriesModel) { + var data = seriesModel.getData(); + if (dataIndex == null) { + dataIndex = data.indexOfName(event.name); + } + var el = data.getItemGraphicEl(dataIndex); + var cx, cy; + // Try to get the point in coordinate system + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.dataToPoint) { + var point = coordSys.dataToPoint( + data.getValues( + zrUtil.map(coordSys.dimensions, function (dim) { + return seriesModel.coordDimToDataDim(dim)[0]; + }), dataIndex, true + ) + ); + cx = point && point[0]; + cy = point && point[1]; + } + else if (el) { + // Use graphic bounding rect + var rect = el.getBoundingRect().clone(); + rect.applyTransform(el.transform); + cx = rect.x + rect.width / 2; + cy = rect.y + rect.height / 2; + } + if (cx != null && cy != null) { + this._tryShow({ + offsetX: cx, + offsetY: cy, + target: el, + event: {} + }); + } + } + } + else { + var el = api.getZr().handler.findHover(event.x, event.y); + this._tryShow({ + offsetX: event.x, + offsetY: event.y, + target: el, + event: {} + }); + } + }, + + _manuallyHideTip: function (e) { + if (e.from === this.uid) { + return; + } + + this._hide(); + }, + + _prepareAxisTriggerData: function (tooltipModel, ecModel) { + // Prepare data for axis trigger + var seriesGroupByAxis = {}; + ecModel.eachSeries(function (seriesModel) { + if (ifSeriesSupportAxisTrigger(seriesModel)) { + var coordSys = seriesModel.coordinateSystem; + var baseAxis; + var key; + + // Only cartesian2d, polar and single support axis trigger + if (coordSys.type === 'cartesian2d') { + // FIXME `axisPointer.axis` is not baseAxis + baseAxis = coordSys.getBaseAxis(); + key = baseAxis.dim + baseAxis.index; + } + else if (coordSys.type === 'singleAxis') { + baseAxis = coordSys.getAxis(); + key = baseAxis.dim + baseAxis.type; + } + else { + baseAxis = coordSys.getBaseAxis(); + key = baseAxis.dim + coordSys.name; + } + + seriesGroupByAxis[key] = seriesGroupByAxis[key] || { + coordSys: [], + series: [] + }; + seriesGroupByAxis[key].coordSys.push(coordSys); + seriesGroupByAxis[key].series.push(seriesModel); + } + }, this); + + return seriesGroupByAxis; + }, + + /** + * mousemove handler + * @param {Object} e + * @private + */ + _tryShow: function (e) { + var el = e.target; + var tooltipModel = this._tooltipModel; + var globalTrigger = tooltipModel.get('trigger'); + var ecModel = this._ecModel; + var api = this._api; + + if (!tooltipModel) { + return; + } + + // Save mouse x, mouse y. So we can try to keep showing the tip if chart is refreshed + this._lastX = e.offsetX; + this._lastY = e.offsetY; + + // Always show item tooltip if mouse is on the element with dataIndex + if (el && el.dataIndex != null) { + // Use dataModel in element if possible + // Used when mouseover on a element like markPoint or edge + // In which case, the data is not main data in series. + var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex); + var dataIndex = el.dataIndex; + var itemModel = dataModel.getData().getItemModel(dataIndex); + // Series or single data may use item trigger when global is axis trigger + if ((itemModel.get('tooltip.trigger') || globalTrigger) === 'axis') { + this._showAxisTooltip(tooltipModel, ecModel, e); + } + else { + // Reset ticket + this._ticket = ''; + // If either single data or series use item trigger + this._hideAxisPointer(); + // Reset last hover and dispatch downplay action + this._resetLastHover(); + + this._showItemTooltipContent(dataModel, dataIndex, el.dataType, e); + } + + api.dispatchAction({ + type: 'showTip', + from: this.uid, + dataIndex: el.dataIndex, + seriesIndex: el.seriesIndex + }); + } + // Tooltip provided directly. Like legend + else if (el && el.tooltip) { + var tooltipOpt = el.tooltip; + if (typeof tooltipOpt === 'string') { + var content = tooltipOpt; + tooltipOpt = { + content: content, + // Fixed formatter + formatter: content + }; + } + var subTooltipModel = new Model(tooltipOpt, tooltipModel); + var defaultHtml = subTooltipModel.get('content'); + var asyncTicket = Math.random(); + this._showTooltipContent( + // TODO params + subTooltipModel, defaultHtml, subTooltipModel.get('formatterParams') || {}, + asyncTicket, e.offsetX, e.offsetY, el, api + ); + } + else { + if (globalTrigger === 'item') { + this._hide(); + } + else { + // Try show axis tooltip + this._showAxisTooltip(tooltipModel, ecModel, e); + } + + // Action of cross pointer + // other pointer types will trigger action in _dispatchAndShowSeriesTooltipContent method + if (tooltipModel.get('axisPointer.type') === 'cross') { + api.dispatchAction({ + type: 'showTip', + from: this.uid, + x: e.offsetX, + y: e.offsetY + }); + } + } + }, + + /** + * Show tooltip on axis + * @param {module:echarts/component/tooltip/TooltipModel} tooltipModel + * @param {module:echarts/model/Global} ecModel + * @param {Object} e + * @private + */ + _showAxisTooltip: function (tooltipModel, ecModel, e) { + var axisPointerModel = tooltipModel.getModel('axisPointer'); + var axisPointerType = axisPointerModel.get('type'); + + if (axisPointerType === 'cross') { + var el = e.target; + if (el && el.dataIndex != null) { + var seriesModel = ecModel.getSeriesByIndex(el.seriesIndex); + var dataIndex = el.dataIndex; + this._showItemTooltipContent(seriesModel, dataIndex, el.dataType, e); + } + } + + this._showAxisPointer(); + var allNotShow = true; + zrUtil.each(this._seriesGroupByAxis, function (seriesCoordSysSameAxis) { + // Try show the axis pointer + var allCoordSys = seriesCoordSysSameAxis.coordSys; + var coordSys = allCoordSys[0]; + + // If mouse position is not in the grid or polar + var point = [e.offsetX, e.offsetY]; + + if (!coordSys.containPoint(point)) { + // Hide axis pointer + this._hideAxisPointer(coordSys.name); + return; + } + + allNotShow = false; + // Make sure point is discrete on cateogry axis + var dimensions = coordSys.dimensions; + var value = coordSys.pointToData(point, true); + point = coordSys.dataToPoint(value); + var baseAxis = coordSys.getBaseAxis(); + var axisType = axisPointerModel.get('axis'); + if (axisType === 'auto') { + axisType = baseAxis.dim; + } + + var contentNotChange = false; + var lastHover = this._lastHover; + if (axisPointerType === 'cross') { + // If hover data not changed + // Possible when two axes are all category + if (dataEqual(lastHover.data, value)) { + contentNotChange = true; + } + lastHover.data = value; + } + else { + var valIndex = zrUtil.indexOf(dimensions, axisType); + + // If hover data not changed on the axis dimension + if (lastHover.data === value[valIndex]) { + contentNotChange = true; + } + lastHover.data = value[valIndex]; + } + + if (coordSys.type === 'cartesian2d' && !contentNotChange) { + this._showCartesianPointer( + axisPointerModel, coordSys, axisType, point + ); + } + else if (coordSys.type === 'polar' && !contentNotChange) { + this._showPolarPointer( + axisPointerModel, coordSys, axisType, point + ); + } + else if (coordSys.type === 'singleAxis' && !contentNotChange) { + this._showSinglePointer( + axisPointerModel, coordSys, axisType, point + ); + } + + if (axisPointerType !== 'cross') { + this._dispatchAndShowSeriesTooltipContent( + coordSys, seriesCoordSysSameAxis.series, point, value, contentNotChange + ); + } + }, this); + + if (!this._tooltipModel.get('show')) { + this._hideAxisPointer(); + } + + if (allNotShow) { + this._hide(); + } + }, + + /** + * Show tooltip on axis of cartesian coordinate + * @param {module:echarts/model/Model} axisPointerModel + * @param {module:echarts/coord/cartesian/Cartesian2D} cartesians + * @param {string} axisType + * @param {Array.} point + * @private + */ + _showCartesianPointer: function (axisPointerModel, cartesian, axisType, point) { + var self = this; + + var axisPointerType = axisPointerModel.get('type'); + var baseAxis = cartesian.getBaseAxis(); + var moveAnimation = axisPointerType !== 'cross' + && baseAxis.type === 'category' + && baseAxis.getBandWidth() > 20; + + if (axisPointerType === 'cross') { + moveGridLine('x', point, cartesian.getAxis('y').getGlobalExtent()); + moveGridLine('y', point, cartesian.getAxis('x').getGlobalExtent()); + + this._updateCrossText(cartesian, point, axisPointerModel); + } + else { + var otherAxis = cartesian.getAxis(axisType === 'x' ? 'y' : 'x'); + var otherExtent = otherAxis.getGlobalExtent(); + + if (cartesian.type === 'cartesian2d') { + (axisPointerType === 'line' ? moveGridLine : moveGridShadow)( + axisType, point, otherExtent + ); + } + } + + /** + * @inner + */ + function moveGridLine(axisType, point, otherExtent) { + var targetShape = axisType === 'x' + ? makeLineShape(point[0], otherExtent[0], point[0], otherExtent[1]) + : makeLineShape(otherExtent[0], point[1], otherExtent[1], point[1]); + + var pointerEl = self._getPointerElement( + cartesian, axisPointerModel, axisType, targetShape + ); + graphic.subPixelOptimizeLine({ + shape: targetShape, + style: pointerEl.style + }); + + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + /** + * @inner + */ + function moveGridShadow(axisType, point, otherExtent) { + var axis = cartesian.getAxis(axisType); + var bandWidth = axis.getBandWidth(); + var span = otherExtent[1] - otherExtent[0]; + var targetShape = axisType === 'x' + ? makeRectShape(point[0] - bandWidth / 2, otherExtent[0], bandWidth, span) + : makeRectShape(otherExtent[0], point[1] - bandWidth / 2, span, bandWidth); + + var pointerEl = self._getPointerElement( + cartesian, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + }, + + _showSinglePointer: function (axisPointerModel, single, axisType, point) { + var self = this; + var axisPointerType = axisPointerModel.get('type'); + var moveAnimation = axisPointerType !== 'cross' && single.getBaseAxis().type === 'category'; + var rect = single.getRect(); + var otherExtent = [rect.y, rect.y + rect.height]; + + moveSingleLine(axisType, point, otherExtent); + + /** + * @inner + */ + function moveSingleLine(axisType, point, otherExtent) { + var axis = single.getAxis(); + var orient = axis.orient; + + var targetShape = orient === 'horizontal' + ? makeLineShape(point[0], otherExtent[0], point[0], otherExtent[1]) + : makeLineShape(otherExtent[0], point[1], otherExtent[1], point[1]); + + var pointerEl = self._getPointerElement( + single, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + }, + + /** + * Show tooltip on axis of polar coordinate + * @param {module:echarts/model/Model} axisPointerModel + * @param {Array.} polar + * @param {string} axisType + * @param {Array.} point + */ + _showPolarPointer: function (axisPointerModel, polar, axisType, point) { + var self = this; + + var axisPointerType = axisPointerModel.get('type'); + + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + + var moveAnimation = axisPointerType !== 'cross' + && polar.getBaseAxis().type === 'category'; + + if (axisPointerType === 'cross') { + movePolarLine('angle', point, radiusAxis.getExtent()); + movePolarLine('radius', point, angleAxis.getExtent()); + + this._updateCrossText(polar, point, axisPointerModel); + } + else { + var otherAxis = polar.getAxis(axisType === 'radius' ? 'angle' : 'radius'); + var otherExtent = otherAxis.getExtent(); + + (axisPointerType === 'line' ? movePolarLine : movePolarShadow)( + axisType, point, otherExtent + ); + } + /** + * @inner + */ + function movePolarLine(axisType, point, otherExtent) { + var mouseCoord = polar.pointToCoord(point); + + var targetShape; + + if (axisType === 'angle') { + var p1 = polar.coordToPoint([otherExtent[0], mouseCoord[1]]); + var p2 = polar.coordToPoint([otherExtent[1], mouseCoord[1]]); + targetShape = makeLineShape(p1[0], p1[1], p2[0], p2[1]); + } + else { + targetShape = { + cx: polar.cx, + cy: polar.cy, + r: mouseCoord[0] + }; + } + + var pointerEl = self._getPointerElement( + polar, axisPointerModel, axisType, targetShape + ); + + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + + /** + * @inner + */ + function movePolarShadow(axisType, point, otherExtent) { + var axis = polar.getAxis(axisType); + var bandWidth = axis.getBandWidth(); + + var mouseCoord = polar.pointToCoord(point); + + var targetShape; + + var radian = Math.PI / 180; + + if (axisType === 'angle') { + targetShape = makeSectorShape( + polar.cx, polar.cy, + otherExtent[0], otherExtent[1], + // In ECharts y is negative if angle is positive + (-mouseCoord[1] - bandWidth / 2) * radian, + (-mouseCoord[1] + bandWidth / 2) * radian + ); + } + else { + targetShape = makeSectorShape( + polar.cx, polar.cy, + mouseCoord[0] - bandWidth / 2, + mouseCoord[0] + bandWidth / 2, + 0, Math.PI * 2 + ); + } + + var pointerEl = self._getPointerElement( + polar, axisPointerModel, axisType, targetShape + ); + moveAnimation + ? graphic.updateProps(pointerEl, { + shape: targetShape + }, axisPointerModel) + : pointerEl.attr({ + shape: targetShape + }); + } + }, + + _updateCrossText: function (coordSys, point, axisPointerModel) { + var crossStyleModel = axisPointerModel.getModel('crossStyle'); + var textStyleModel = crossStyleModel.getModel('textStyle'); + + var tooltipModel = this._tooltipModel; + + var text = this._crossText; + if (!text) { + text = this._crossText = new graphic.Text({ + style: { + textAlign: 'left', + textVerticalAlign: 'bottom' + } + }); + this.group.add(text); + } + + var value = coordSys.pointToData(point); + + var dims = coordSys.dimensions; + value = zrUtil.map(value, function (val, idx) { + var axis = coordSys.getAxis(dims[idx]); + if (axis.type === 'category' || axis.type === 'time') { + val = axis.scale.getLabel(val); + } + else { + val = formatUtil.addCommas( + val.toFixed(axis.getPixelPrecision()) + ); + } + return val; + }); + + text.setStyle({ + fill: textStyleModel.getTextColor() || crossStyleModel.get('color'), + textFont: textStyleModel.getFont(), + text: value.join(', '), + x: point[0] + 5, + y: point[1] - 5 + }); + text.z = tooltipModel.get('z'); + text.zlevel = tooltipModel.get('zlevel'); + }, + + _getPointerElement: function (coordSys, pointerModel, axisType, initShape) { + var tooltipModel = this._tooltipModel; + var z = tooltipModel.get('z'); + var zlevel = tooltipModel.get('zlevel'); + var axisPointers = this._axisPointers; + var coordSysName = coordSys.name; + axisPointers[coordSysName] = axisPointers[coordSysName] || {}; + if (axisPointers[coordSysName][axisType]) { + return axisPointers[coordSysName][axisType]; + } + + // Create if not exists + var pointerType = pointerModel.get('type'); + var styleModel = pointerModel.getModel(pointerType + 'Style'); + var isShadow = pointerType === 'shadow'; + var style = styleModel[isShadow ? 'getAreaStyle' : 'getLineStyle'](); + + var elementType = coordSys.type === 'polar' + ? (isShadow ? 'Sector' : (axisType === 'radius' ? 'Circle' : 'Line')) + : (isShadow ? 'Rect' : 'Line'); + + isShadow ? (style.stroke = null) : (style.fill = null); + + var el = axisPointers[coordSysName][axisType] = new graphic[elementType]({ + style: style, + z: z, + zlevel: zlevel, + silent: true, + shape: initShape + }); + + this.group.add(el); + return el; + }, + + /** + * Dispatch actions and show tooltip on series + * @param {Array.} seriesList + * @param {Array.} point + * @param {Array.} value + * @param {boolean} contentNotChange + * @param {Object} e + */ + _dispatchAndShowSeriesTooltipContent: function ( + coordSys, seriesList, point, value, contentNotChange + ) { + + var rootTooltipModel = this._tooltipModel; + + var baseAxis = coordSys.getBaseAxis(); + var baseDimIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; + + var payloadBatch = zrUtil.map(seriesList, function (series) { + return { + seriesIndex: series.seriesIndex, + dataIndex: series.getAxisTooltipDataIndex + ? series.getAxisTooltipDataIndex(series.coordDimToDataDim(baseAxis.dim), value, baseAxis) + : series.getData().indexOfNearest( + series.coordDimToDataDim(baseAxis.dim)[0], + value[baseDimIndex], + // Add a threshold to avoid find the wrong dataIndex when data length is not same + false, baseAxis.type === 'category' ? 0.5 : null + ) + }; + }); + + var lastHover = this._lastHover; + var api = this._api; + // Dispatch downplay action + if (lastHover.payloadBatch && !contentNotChange) { + api.dispatchAction({ + type: 'downplay', + batch: lastHover.payloadBatch + }); + } + // Dispatch highlight action + if (!contentNotChange) { + api.dispatchAction({ + type: 'highlight', + batch: payloadBatch + }); + lastHover.payloadBatch = payloadBatch; + } + // Dispatch showTip action + api.dispatchAction({ + type: 'showTip', + dataIndex: payloadBatch[0].dataIndex, + seriesIndex: payloadBatch[0].seriesIndex, + from: this.uid + }); + + if (baseAxis && rootTooltipModel.get('showContent') && rootTooltipModel.get('show')) { + var paramsList = zrUtil.map(seriesList, function (series, index) { + return series.getDataParams(payloadBatch[index].dataIndex); + }); + + if (!contentNotChange) { + // Update html content + var firstDataIndex = payloadBatch[0].dataIndex; + + // Default tooltip content + // FIXME + // (1) shold be the first data which has name? + // (2) themeRiver, firstDataIndex is array, and first line is unnecessary. + var firstLine = baseAxis.type === 'time' + ? baseAxis.scale.getLabel(value[baseDimIndex]) + : seriesList[0].getData().getName(firstDataIndex); + var defaultHtml = (firstLine ? firstLine + '
' : '') + + zrUtil.map(seriesList, function (series, index) { + return series.formatTooltip(payloadBatch[index].dataIndex, true); + }).join('
'); + + var asyncTicket = 'axis_' + coordSys.name + '_' + firstDataIndex; + + this._showTooltipContent( + rootTooltipModel, defaultHtml, paramsList, asyncTicket, + point[0], point[1], null, api + ); + } + else { + updatePosition( + rootTooltipModel.get('position'), point[0], point[1], + this._tooltipContent, paramsList, null, api + ); + } + } + }, + + /** + * Show tooltip on item + * @param {module:echarts/model/Series} seriesModel + * @param {number} dataIndex + * @param {string} dataType + * @param {Object} e + */ + _showItemTooltipContent: function (seriesModel, dataIndex, dataType, e) { + // FIXME Graph data + var api = this._api; + var data = seriesModel.getData(dataType); + var itemModel = data.getItemModel(dataIndex); + + var tooltipOpt = itemModel.get('tooltip', true); + if (typeof tooltipOpt === 'string') { + // In each data item tooltip can be simply write: + // { + // value: 10, + // tooltip: 'Something you need to know' + // } + var tooltipContent = tooltipOpt; + tooltipOpt = { + formatter: tooltipContent + }; + } + var rootTooltipModel = this._tooltipModel; + var seriesTooltipModel = seriesModel.getModel( + 'tooltip', rootTooltipModel + ); + var tooltipModel = new Model(tooltipOpt, seriesTooltipModel, seriesTooltipModel.ecModel); + + var params = seriesModel.getDataParams(dataIndex, dataType); + var defaultHtml = seriesModel.formatTooltip(dataIndex, false, dataType); + + var asyncTicket = 'item_' + seriesModel.name + '_' + dataIndex; + + this._showTooltipContent( + tooltipModel, defaultHtml, params, asyncTicket, + e.offsetX, e.offsetY, e.target, api + ); + }, + + _showTooltipContent: function ( + tooltipModel, defaultHtml, params, asyncTicket, x, y, target, api + ) { + // Reset ticket + this._ticket = ''; + + if (tooltipModel.get('showContent') && tooltipModel.get('show')) { + var tooltipContent = this._tooltipContent; + + var formatter = tooltipModel.get('formatter'); + var positionExpr = tooltipModel.get('position'); + var html = defaultHtml; + + if (formatter) { + if (typeof formatter === 'string') { + html = formatUtil.formatTpl(formatter, params); + } + else if (typeof formatter === 'function') { + var self = this; + var ticket = asyncTicket; + var callback = function (cbTicket, html) { + if (cbTicket === self._ticket) { + tooltipContent.setContent(html); + + updatePosition( + positionExpr, x, y, + tooltipContent, params, target, api + ); + } + }; + self._ticket = ticket; + html = formatter(params, ticket, callback); + } + } + + tooltipContent.show(tooltipModel); + tooltipContent.setContent(html); + + updatePosition( + positionExpr, x, y, + tooltipContent, params, target, api + ); + } + }, + + /** + * Show axis pointer + * @param {string} [coordSysName] + */ + _showAxisPointer: function (coordSysName) { + if (coordSysName) { + var axisPointers = this._axisPointers[coordSysName]; + axisPointers && zrUtil.each(axisPointers, function (el) { + el.show(); + }); + } + else { + this.group.eachChild(function (child) { + child.show(); + }); + this.group.show(); + } + }, + + _resetLastHover: function () { + var lastHover = this._lastHover; + if (lastHover.payloadBatch) { + this._api.dispatchAction({ + type: 'downplay', + batch: lastHover.payloadBatch + }); + } + // Reset lastHover + this._lastHover = {}; + }, + /** + * Hide axis pointer + * @param {string} [coordSysName] + */ + _hideAxisPointer: function (coordSysName) { + if (coordSysName) { + var axisPointers = this._axisPointers[coordSysName]; + axisPointers && zrUtil.each(axisPointers, function (el) { + el.hide(); + }); + } + else { + if (this.group.children().length) { + this.group.hide(); + } + } + }, + + _hide: function () { + clearTimeout(this._showTimeout); + + this._hideAxisPointer(); + this._resetLastHover(); + if (!this._alwaysShowContent) { + this._tooltipContent.hideLater(this._tooltipModel.get('hideDelay')); + } + + this._api.dispatchAction({ + type: 'hideTip', + from: this.uid + }); + + this._lastX = this._lastY = null; + }, + + dispose: function (ecModel, api) { + if (env.node) { + return; + } + var zr = api.getZr(); + this._tooltipContent.hide(); + + zr.off('click', this._tryShow); + zr.off('mousemove', this._mousemove); + zr.off('mouseout', this._hide); + zr.off('globalout', this._hide); + + api.off('showTip', this._manuallyShowTip); + api.off('hideTip', this._manuallyHideTip); + } + }); + + +/***/ }, +/* 282 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/tooltip/TooltipContent + */ + + + var zrUtil = __webpack_require__(4); + var zrColor = __webpack_require__(39); + var eventUtil = __webpack_require__(87); + var formatUtil = __webpack_require__(6); + var each = zrUtil.each; + var toCamelCase = formatUtil.toCamelCase; + var env = __webpack_require__(2); + + var vendors = ['', '-webkit-', '-moz-', '-o-']; + + var gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;'; + + /** + * @param {number} duration + * @return {string} + * @inner + */ + function assembleTransition(duration) { + var transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)'; + var transitionText = 'left ' + duration + 's ' + transitionCurve + ',' + + 'top ' + duration + 's ' + transitionCurve; + return zrUtil.map(vendors, function (vendorPrefix) { + return vendorPrefix + 'transition:' + transitionText; + }).join(';'); + } + + /** + * @param {Object} textStyle + * @return {string} + * @inner + */ + function assembleFont(textStyleModel) { + var cssText = []; + + var fontSize = textStyleModel.get('fontSize'); + var color = textStyleModel.getTextColor(); + + color && cssText.push('color:' + color); + + cssText.push('font:' + textStyleModel.getFont()); + + fontSize && + cssText.push('line-height:' + Math.round(fontSize * 3 / 2) + 'px'); + + each(['decoration', 'align'], function (name) { + var val = textStyleModel.get(name); + val && cssText.push('text-' + name + ':' + val); + }); + + return cssText.join(';'); + } + + /** + * @param {Object} tooltipModel + * @return {string} + * @inner + */ + function assembleCssText(tooltipModel) { + + tooltipModel = tooltipModel; + + var cssText = []; + + var transitionDuration = tooltipModel.get('transitionDuration'); + var backgroundColor = tooltipModel.get('backgroundColor'); + var textStyleModel = tooltipModel.getModel('textStyle'); + var padding = tooltipModel.get('padding'); + + // Animation transition + transitionDuration && + cssText.push(assembleTransition(transitionDuration)); + + if (backgroundColor) { + if (env.canvasSupported) { + cssText.push('background-Color:' + backgroundColor); + } + else { + // for ie + cssText.push( + 'background-Color:#' + zrColor.toHex(backgroundColor) + ); + cssText.push('filter:alpha(opacity=70)'); + } + } + + // Border style + each(['width', 'color', 'radius'], function (name) { + var borderName = 'border-' + name; + var camelCase = toCamelCase(borderName); + var val = tooltipModel.get(camelCase); + val != null && + cssText.push(borderName + ':' + val + (name === 'color' ? '' : 'px')); + }); + + // Text style + cssText.push(assembleFont(textStyleModel)); + + // Padding + if (padding != null) { + cssText.push('padding:' + formatUtil.normalizeCssArray(padding).join('px ') + 'px'); + } + + return cssText.join(';') + ';'; + } + + /** + * @alias module:echarts/component/tooltip/TooltipContent + * @constructor + */ + function TooltipContent(container, api) { + var el = document.createElement('div'); + var zr = api.getZr(); + + this.el = el; + + this._x = api.getWidth() / 2; + this._y = api.getHeight() / 2; + + container.appendChild(el); + + this._container = container; + + this._show = false; + + /** + * @private + */ + this._hideTimeout; + + var self = this; + el.onmouseenter = function () { + // clear the timeout in hideLater and keep showing tooltip + if (self.enterable) { + clearTimeout(self._hideTimeout); + self._show = true; + } + self._inContent = true; + }; + el.onmousemove = function (e) { + if (!self.enterable) { + // Try trigger zrender event to avoid mouse + // in and out shape too frequently + var handler = zr.handler; + eventUtil.normalizeEvent(container, e); + handler.dispatch('mousemove', e); + } + }; + el.onmouseleave = function () { + if (self.enterable) { + if (self._show) { + self.hideLater(self._hideDelay); + } + } + self._inContent = false; + }; + + compromiseMobile(el, container); + } + + function compromiseMobile(tooltipContentEl, container) { + // Prevent default behavior on mobile. For example, + // default pinch gesture will cause browser zoom. + // We do not preventing event on tooltip contnet el, + // because user may need customization in tooltip el. + eventUtil.addEventListener(container, 'touchstart', preventDefault); + eventUtil.addEventListener(container, 'touchmove', preventDefault); + eventUtil.addEventListener(container, 'touchend', preventDefault); + + function preventDefault(e) { + if (contains(e.target)) { + e.preventDefault(); + } + } + + function contains(targetEl) { + while (targetEl && targetEl !== container) { + if (targetEl === tooltipContentEl) { + return true; + } + targetEl = targetEl.parentNode; + } + } + } + + TooltipContent.prototype = { + + constructor: TooltipContent, + + enterable: true, + + /** + * Update when tooltip is rendered + */ + update: function () { + var container = this._container; + var stl = container.currentStyle + || document.defaultView.getComputedStyle(container); + var domStyle = container.style; + if (domStyle.position !== 'absolute' && stl.position !== 'absolute') { + domStyle.position = 'relative'; + } + // Hide the tooltip + // PENDING + // this.hide(); + }, + + show: function (tooltipModel) { + clearTimeout(this._hideTimeout); + var el = this.el; + + el.style.cssText = gCssText + assembleCssText(tooltipModel) + // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore + + ';left:' + this._x + 'px;top:' + this._y + 'px;' + + (tooltipModel.get('extraCssText') || ''); + + el.style.display = el.innerHTML ? 'block' : 'none'; + + this._show = true; + }, + + setContent: function (content) { + var el = this.el; + el.innerHTML = content; + el.style.display = content ? 'block' : 'none'; + }, + + moveTo: function (x, y) { + var style = this.el.style; + style.left = x + 'px'; + style.top = y + 'px'; + + this._x = x; + this._y = y; + }, + + hide: function () { + this.el.style.display = 'none'; + this._show = false; + }, + + // showLater: function () + + hideLater: function (time) { + if (this._show && !(this._inContent && this.enterable)) { + if (time) { + this._hideDelay = time; + // Set show false to avoid invoke hideLater mutiple times + this._show = false; + this._hideTimeout = setTimeout(zrUtil.bind(this.hide, this), time); + } + else { + this.hide(); + } + } + }, + + isShow: function () { + return this._show; + } + }; + + module.exports = TooltipContent; + + +/***/ }, +/* 283 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(284); + __webpack_require__(290); + __webpack_require__(292); + + // Polar view + __webpack_require__(1).extendComponentView({ + type: 'polar' + }); + + +/***/ }, +/* 284 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Axis scale + + + var Polar = __webpack_require__(285); + var numberUtil = __webpack_require__(7); + var zrUtil = __webpack_require__(4); + + var axisHelper = __webpack_require__(114); + var niceScaleExtent = axisHelper.niceScaleExtent; + + // 依赖 PolarModel 做预处理 + __webpack_require__(288); + + /** + * Resize method bound to the polar + * @param {module:echarts/coord/polar/PolarModel} polarModel + * @param {module:echarts/ExtensionAPI} api + */ + function resizePolar(polarModel, api) { + var center = polarModel.get('center'); + var radius = polarModel.get('radius'); + var width = api.getWidth(); + var height = api.getHeight(); + var parsePercent = numberUtil.parsePercent; + + this.cx = parsePercent(center[0], width); + this.cy = parsePercent(center[1], height); + + var radiusAxis = this.getRadiusAxis(); + var size = Math.min(width, height) / 2; + // var idx = radiusAxis.inverse ? 1 : 0; + radiusAxis.setExtent(0, parsePercent(radius, size)); + } + + /** + * Update polar + */ + function updatePolarScale(ecModel, api) { + var polar = this; + var angleAxis = polar.getAngleAxis(); + var radiusAxis = polar.getRadiusAxis(); + // Reset scale + angleAxis.scale.setExtent(Infinity, -Infinity); + radiusAxis.scale.setExtent(Infinity, -Infinity); + + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.coordinateSystem === polar) { + var data = seriesModel.getData(); + radiusAxis.scale.unionExtent( + data.getDataExtent('radius', radiusAxis.type !== 'category') + ); + angleAxis.scale.unionExtent( + data.getDataExtent('angle', angleAxis.type !== 'category') + ); + } + }); + + niceScaleExtent(angleAxis, angleAxis.model); + niceScaleExtent(radiusAxis, radiusAxis.model); + + // Fix extent of category angle axis + if (angleAxis.type === 'category' && !angleAxis.onBand) { + var extent = angleAxis.getExtent(); + var diff = 360 / angleAxis.scale.count(); + angleAxis.inverse ? (extent[1] += diff) : (extent[1] -= diff); + angleAxis.setExtent(extent[0], extent[1]); + } + } + + /** + * Set common axis properties + * @param {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + * @param {module:echarts/coord/polar/AxisModel} + * @inner + */ + function setAxis(axis, axisModel) { + axis.type = axisModel.get('type'); + axis.scale = axisHelper.createScaleByModel(axisModel); + axis.onBand = axisModel.get('boundaryGap') && axis.type === 'category'; + + // FIXME Radius axis not support inverse axis + if (axisModel.mainType === 'angleAxis') { + var startAngle = axisModel.get('startAngle'); + axis.inverse = axisModel.get('inverse') ^ axisModel.get('clockwise'); + axis.setExtent(startAngle, startAngle + (axis.inverse ? -360 : 360)); + } + + // Inject axis instance + axisModel.axis = axis; + axis.model = axisModel; + } + + + var polarCreator = { + + dimensions: Polar.prototype.dimensions, + + create: function (ecModel, api) { + var polarList = []; + ecModel.eachComponent('polar', function (polarModel, idx) { + var polar = new Polar(idx); + // Inject resize and update method + polar.resize = resizePolar; + polar.update = updatePolarScale; + + var radiusAxis = polar.getRadiusAxis(); + var angleAxis = polar.getAngleAxis(); + + var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); + var angleAxisModel = polarModel.findAxisModel('angleAxis'); + + setAxis(radiusAxis, radiusAxisModel); + setAxis(angleAxis, angleAxisModel); + + polar.resize(polarModel, api); + polarList.push(polar); + + polarModel.coordinateSystem = polar; + }); + // Inject coordinateSystem to series + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'polar') { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: seriesModel.get('polarIndex'), + id: seriesModel.get('polarId') + })[0]; + + if (true) { + if (!polarModel) { + throw new Error( + 'Polar "' + zrUtil.retrieve( + seriesModel.get('polarIndex'), + seriesModel.get('polarId'), + 0 + ) + '" not found' + ); + } + } + seriesModel.coordinateSystem = polarModel.coordinateSystem; + } + }); + + return polarList; + } + }; + + __webpack_require__(26).register('polar', polarCreator); + + +/***/ }, +/* 285 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + /** + * @module echarts/coord/polar/Polar + */ + + + var RadiusAxis = __webpack_require__(286); + var AngleAxis = __webpack_require__(287); + + /** + * @alias {module:echarts/coord/polar/Polar} + * @constructor + * @param {string} name + */ + var Polar = function (name) { + + /** + * @type {string} + */ + this.name = name || ''; + + /** + * x of polar center + * @type {number} + */ + this.cx = 0; + + /** + * y of polar center + * @type {number} + */ + this.cy = 0; + + /** + * @type {module:echarts/coord/polar/RadiusAxis} + * @private + */ + this._radiusAxis = new RadiusAxis(); + + /** + * @type {module:echarts/coord/polar/AngleAxis} + * @private + */ + this._angleAxis = new AngleAxis(); + }; + + Polar.prototype = { + + constructor: Polar, + + type: 'polar', + + /** + * @param {Array.} + * @readOnly + */ + dimensions: ['radius', 'angle'], + + /** + * If contain coord + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var coord = this.pointToCoord(point); + return this._radiusAxis.contain(coord[0]) + && this._angleAxis.contain(coord[1]); + }, + + /** + * If contain data + * @param {Array.} data + * @return {boolean} + */ + containData: function (data) { + return this._radiusAxis.containData(data[0]) + && this._angleAxis.containData(data[1]); + }, + + /** + * @param {string} axisType + * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + getAxis: function (axisType) { + return this['_' + axisType + 'Axis']; + }, + + /** + * Get axes by type of scale + * @param {string} scaleType + * @return {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + getAxesByScale: function (scaleType) { + var axes = []; + var angleAxis = this._angleAxis; + var radiusAxis = this._radiusAxis; + angleAxis.scale.type === scaleType && axes.push(angleAxis); + radiusAxis.scale.type === scaleType && axes.push(radiusAxis); + + return axes; + }, + + /** + * @return {module:echarts/coord/polar/AngleAxis} + */ + getAngleAxis: function () { + return this._angleAxis; + }, + + /** + * @return {module:echarts/coord/polar/RadiusAxis} + */ + getRadiusAxis: function () { + return this._radiusAxis; + }, + + /** + * @param {module:echarts/coord/polar/Axis} + * @return {module:echarts/coord/polar/Axis} + */ + getOtherAxis: function (axis) { + var angleAxis = this._angleAxis; + return axis === angleAxis ? this._radiusAxis : angleAxis; + }, + + /** + * Base axis will be used on stacking. + * + * @return {module:echarts/coord/polar/Axis} + */ + getBaseAxis: function () { + return this.getAxesByScale('ordinal')[0] + || this.getAxesByScale('time')[0] + || this.getAngleAxis(); + }, + + /** + * Convert series data to a list of (x, y) points + * @param {module:echarts/data/List} data + * @return {Array} + * Return list of coordinates. For example: + * `[[10, 10], [20, 20], [30, 30]]` + */ + dataToPoints: function (data) { + return data.mapArray(this.dimensions, function (radius, angle) { + return this.dataToPoint([radius, angle]); + }, this); + }, + + /** + * Convert a single data item to (x, y) point. + * Parameter data is an array which the first element is radius and the second is angle + * @param {Array.} data + * @param {boolean} [clamp=false] + * @return {Array.} + */ + dataToPoint: function (data, clamp) { + return this.coordToPoint([ + this._radiusAxis.dataToRadius(data[0], clamp), + this._angleAxis.dataToAngle(data[1], clamp) + ]); + }, + + /** + * Convert a (x, y) point to data + * @param {Array.} point + * @param {boolean} [clamp=false] + * @return {Array.} + */ + pointToData: function (point, clamp) { + var coord = this.pointToCoord(point); + return [ + this._radiusAxis.radiusToData(coord[0], clamp), + this._angleAxis.angleToData(coord[1], clamp) + ]; + }, + + /** + * Convert a (x, y) point to (radius, angle) coord + * @param {Array.} point + * @return {Array.} + */ + pointToCoord: function (point) { + var dx = point[0] - this.cx; + var dy = point[1] - this.cy; + var angleAxis = this.getAngleAxis(); + var extent = angleAxis.getExtent(); + var minAngle = Math.min(extent[0], extent[1]); + var maxAngle = Math.max(extent[0], extent[1]); + // Fix fixed extent in polarCreator + // FIXME + angleAxis.inverse + ? (minAngle = maxAngle - 360) + : (maxAngle = minAngle + 360); + + var radius = Math.sqrt(dx * dx + dy * dy); + dx /= radius; + dy /= radius; + + var radian = Math.atan2(-dy, dx) / Math.PI * 180; + + // move to angleExtent + var dir = radian < minAngle ? 1 : -1; + while (radian < minAngle || radian > maxAngle) { + radian += dir * 360; + } + + return [radius, radian]; + }, + + /** + * Convert a (radius, angle) coord to (x, y) point + * @param {Array.} coord + * @return {Array.} + */ + coordToPoint: function (coord) { + var radius = coord[0]; + var radian = coord[1] / 180 * Math.PI; + var x = Math.cos(radian) * radius + this.cx; + // Inverse the y + var y = -Math.sin(radian) * radius + this.cy; + + return [x, y]; + } + }; + + module.exports = Polar; + + +/***/ }, +/* 286 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function RadiusAxis(scale, radiusExtent) { + + Axis.call(this, 'radius', scale, radiusExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'category'; + } + + RadiusAxis.prototype = { + + constructor: RadiusAxis, + + dataToRadius: Axis.prototype.dataToCoord, + + radiusToData: Axis.prototype.coordToData + }; + + zrUtil.inherits(RadiusAxis, Axis); + + module.exports = RadiusAxis; + + +/***/ }, +/* 287 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + + function AngleAxis(scale, angleExtent) { + + angleExtent = angleExtent || [0, 360]; + + Axis.call(this, 'angle', scale, angleExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = 'category'; + } + + AngleAxis.prototype = { + + constructor: AngleAxis, + + dataToAngle: Axis.prototype.dataToCoord, + + angleToData: Axis.prototype.coordToData + }; + + zrUtil.inherits(AngleAxis, Axis); + + module.exports = AngleAxis; + + +/***/ }, +/* 288 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(289); + + __webpack_require__(1).extendComponentModel({ + + type: 'polar', + + dependencies: ['polarAxis', 'angleAxis'], + + /** + * @type {module:echarts/coord/polar/Polar} + */ + coordinateSystem: null, + + /** + * @param {string} axisType + * @return {module:echarts/coord/polar/AxisModel} + */ + findAxisModel: function (axisType) { + var foundAxisModel; + var ecModel = this.ecModel; + + ecModel.eachComponent(axisType, function (axisModel) { + var polarModel = ecModel.queryComponents({ + mainType: 'polar', + index: axisModel.getShallow('polarIndex'), + id: axisModel.getShallow('polarId') + })[0]; + + if(polarModel === this) { + foundAxisModel = axisModel; + } + }, this); + return foundAxisModel; + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + center: ['50%', '50%'], + + radius: '80%' + } + }); + + +/***/ }, +/* 289 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var ComponentModel = __webpack_require__(19); + var axisModelCreator = __webpack_require__(127); + + var PolarAxisModel = ComponentModel.extend({ + type: 'polarAxis', + /** + * @type {module:echarts/coord/polar/AngleAxis|module:echarts/coord/polar/RadiusAxis} + */ + axis: null + }); + + zrUtil.merge(PolarAxisModel.prototype, __webpack_require__(129)); + + var polarAxisDefaultExtendedOption = { + angle: { + // polarIndex: 0, + // polarId: '', + + startAngle: 90, + + clockwise: true, + + splitNumber: 12, + + axisLabel: { + rotate: false + } + }, + radius: { + // polarIndex: 0, + // polarId: '', + + splitNumber: 5 + } + }; + + function getAxisType(axisDim, option) { + // Default axis with data is category axis + return option.type || (option.data ? 'category' : 'value'); + } + + axisModelCreator('angle', PolarAxisModel, getAxisType, polarAxisDefaultExtendedOption.angle); + axisModelCreator('radius', PolarAxisModel, getAxisType, polarAxisDefaultExtendedOption.radius); + + + +/***/ }, +/* 290 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + __webpack_require__(284); + + __webpack_require__(291); + + +/***/ }, +/* 291 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + + var elementList = ['axisLine', 'axisLabel', 'axisTick', 'splitLine', 'splitArea']; + + function getAxisLineShape(polar, r0, r, angle) { + var start = polar.coordToPoint([r0, angle]); + var end = polar.coordToPoint([r, angle]); + + return { + x1: start[0], + y1: start[1], + x2: end[0], + y2: end[1] + }; + } + __webpack_require__(1).extendComponentView({ + + type: 'angleAxis', + + render: function (angleAxisModel, ecModel) { + this.group.removeAll(); + if (!angleAxisModel.get('show')) { + return; + } + + var polarModel = ecModel.getComponent('polar', angleAxisModel.get('polarIndex')); + var angleAxis = angleAxisModel.axis; + var polar = polarModel.coordinateSystem; + var radiusExtent = polar.getRadiusAxis().getExtent(); + var ticksAngles = angleAxis.getTicksCoords(); + + if (angleAxis.type !== 'category') { + // Remove the last tick which will overlap the first tick + ticksAngles.pop(); + } + + zrUtil.each(elementList, function (name) { + if (angleAxisModel.get(name +'.show')) { + this['_' + name](angleAxisModel, polar, ticksAngles, radiusExtent); + } + }, this); + }, + + /** + * @private + */ + _axisLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var lineStyleModel = angleAxisModel.getModel('axisLine.lineStyle'); + + var circle = new graphic.Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: radiusExtent[1] + }, + style: lineStyleModel.getLineStyle(), + z2: 1, + silent: true + }); + circle.style.fill = null; + + this.group.add(circle); + }, + + /** + * @private + */ + _axisTick: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var tickModel = angleAxisModel.getModel('axisTick'); + + var tickLen = (tickModel.get('inside') ? -1 : 1) * tickModel.get('length'); + + var lines = zrUtil.map(ticksAngles, function (tickAngle) { + return new graphic.Line({ + shape: getAxisLineShape(polar, radiusExtent[1], radiusExtent[1] + tickLen, tickAngle) + }); + }); + this.group.add(graphic.mergePath( + lines, { + style: zrUtil.defaults( + tickModel.getModel('lineStyle').getLineStyle(), + { + stroke: angleAxisModel.get('axisLine.lineStyle.color') + } + ) + } + )); + }, + + /** + * @private + */ + _axisLabel: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var axis = angleAxisModel.axis; + + var categoryData = angleAxisModel.get('data'); + + var labelModel = angleAxisModel.getModel('axisLabel'); + var axisTextStyleModel = labelModel.getModel('textStyle'); + + var labels = angleAxisModel.getFormattedLabels(); + + var labelMargin = labelModel.get('margin'); + var labelsAngles = axis.getLabelsCoords(); + + // Use length of ticksAngles because it may remove the last tick to avoid overlapping + for (var i = 0; i < ticksAngles.length; i++) { + var r = radiusExtent[1]; + var p = polar.coordToPoint([r + labelMargin, labelsAngles[i]]); + var cx = polar.cx; + var cy = polar.cy; + + var labelTextAlign = Math.abs(p[0] - cx) / r < 0.3 + ? 'center' : (p[0] > cx ? 'left' : 'right'); + var labelTextBaseline = Math.abs(p[1] - cy) / r < 0.3 + ? 'middle' : (p[1] > cy ? 'top' : 'bottom'); + + var textStyleModel = axisTextStyleModel; + if (categoryData && categoryData[i] && categoryData[i].textStyle) { + textStyleModel = new Model( + categoryData[i].textStyle, axisTextStyleModel + ); + } + this.group.add(new graphic.Text({ + style: { + x: p[0], + y: p[1], + fill: textStyleModel.getTextColor() || angleAxisModel.get('axisLine.lineStyle.color'), + text: labels[i], + textAlign: labelTextAlign, + textVerticalAlign: labelTextBaseline, + textFont: textStyleModel.getFont() + }, + silent: true + })); + } + }, + + /** + * @private + */ + _splitLine: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + var splitLineModel = angleAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var splitLines = []; + + for (var i = 0; i < ticksAngles.length; i++) { + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Line({ + shape: getAxisLineShape(polar, radiusExtent[0], radiusExtent[1], ticksAngles[i]) + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(graphic.mergePath(splitLines[i], { + style: zrUtil.defaults({ + stroke: lineColors[i % lineColors.length] + }, lineStyleModel.getLineStyle()), + silent: true, + z: angleAxisModel.get('z') + })); + } + }, + + /** + * @private + */ + _splitArea: function (angleAxisModel, polar, ticksAngles, radiusExtent) { + + var splitAreaModel = angleAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + + var splitAreas = []; + + var RADIAN = Math.PI / 180; + var prevAngle = -ticksAngles[0] * RADIAN; + var r0 = Math.min(radiusExtent[0], radiusExtent[1]); + var r1 = Math.max(radiusExtent[0], radiusExtent[1]); + + var clockwise = angleAxisModel.get('clockwise'); + + for (var i = 1; i < ticksAngles.length; i++) { + var colorIndex = (lineCount++) % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: r0, + r: r1, + startAngle: prevAngle, + endAngle: -ticksAngles[i] * RADIAN, + clockwise: clockwise + }, + silent: true + })); + prevAngle = -ticksAngles[i] * RADIAN; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(graphic.mergePath(splitAreas[i], { + style: zrUtil.defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }); + + +/***/ }, +/* 292 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(284); + + __webpack_require__(293); + + +/***/ }, +/* 293 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var AxisBuilder = __webpack_require__(132); + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + var selfBuilderAttrs = [ + 'splitLine', 'splitArea' + ]; + + __webpack_require__(1).extendComponentView({ + + type: 'radiusAxis', + + render: function (radiusAxisModel, ecModel) { + this.group.removeAll(); + if (!radiusAxisModel.get('show')) { + return; + } + var polarModel = ecModel.getComponent('polar', radiusAxisModel.get('polarIndex')); + var angleAxis = polarModel.coordinateSystem.getAngleAxis(); + var radiusAxis = radiusAxisModel.axis; + var polar = polarModel.coordinateSystem; + var ticksCoords = radiusAxis.getTicksCoords(); + var axisAngle = angleAxis.getExtent()[0]; + var radiusExtent = radiusAxis.getExtent(); + + var layout = layoutAxis(polar, radiusAxisModel, axisAngle); + var axisBuilder = new AxisBuilder(radiusAxisModel, layout); + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + this.group.add(axisBuilder.getGroup()); + + zrUtil.each(selfBuilderAttrs, function (name) { + if (radiusAxisModel.get(name +'.show')) { + this['_' + name](radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords); + } + }, this); + }, + + /** + * @private + */ + _splitLine: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + var splitLineModel = radiusAxisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineColors = lineStyleModel.get('color'); + var lineCount = 0; + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var splitLines = []; + + for (var i = 0; i < ticksCoords.length; i++) { + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Circle({ + shape: { + cx: polar.cx, + cy: polar.cy, + r: ticksCoords[i] + }, + silent: true + })); + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitLines.length; i++) { + this.group.add(graphic.mergePath(splitLines[i], { + style: zrUtil.defaults({ + stroke: lineColors[i % lineColors.length], + fill: null + }, lineStyleModel.getLineStyle()), + silent: true + })); + } + }, + + /** + * @private + */ + _splitArea: function (radiusAxisModel, polar, axisAngle, radiusExtent, ticksCoords) { + + var splitAreaModel = radiusAxisModel.getModel('splitArea'); + var areaStyleModel = splitAreaModel.getModel('areaStyle'); + var areaColors = areaStyleModel.get('color'); + var lineCount = 0; + + areaColors = areaColors instanceof Array ? areaColors : [areaColors]; + + var splitAreas = []; + + var prevRadius = ticksCoords[0]; + for (var i = 1; i < ticksCoords.length; i++) { + var colorIndex = (lineCount++) % areaColors.length; + splitAreas[colorIndex] = splitAreas[colorIndex] || []; + splitAreas[colorIndex].push(new graphic.Sector({ + shape: { + cx: polar.cx, + cy: polar.cy, + r0: prevRadius, + r: ticksCoords[i], + startAngle: 0, + endAngle: Math.PI * 2 + }, + silent: true + })); + prevRadius = ticksCoords[i]; + } + + // Simple optimization + // Batching the lines if color are the same + for (var i = 0; i < splitAreas.length; i++) { + this.group.add(graphic.mergePath(splitAreas[i], { + style: zrUtil.defaults({ + fill: areaColors[i % areaColors.length] + }, areaStyleModel.getAreaStyle()), + silent: true + })); + } + } + }); + + /** + * @inner + */ + function layoutAxis(polar, radiusAxisModel, axisAngle) { + return { + position: [polar.cx, polar.cy], + rotation: axisAngle / 180 * Math.PI, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1, + labelRotation: radiusAxisModel.getModel('axisLabel').get('rotate'), + // Over splitLine and splitArea + z2: 1 + }; + } + + +/***/ }, +/* 294 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(295); + + __webpack_require__(163); + + __webpack_require__(296); + + __webpack_require__(176); + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + + function makeAction(method, actionInfo) { + actionInfo.update = 'updateView'; + echarts.registerAction(actionInfo, function (payload, ecModel) { + var selected = {}; + + ecModel.eachComponent( + { mainType: 'geo', query: payload}, + function (geoModel) { + geoModel[method](payload.name); + var geo = geoModel.coordinateSystem; + zrUtil.each(geo.regions, function (region) { + selected[region.name] = geoModel.isSelected(region.name) || false; + }); + } + ); + + return { + selected: selected, + name: payload.name + } + }); + } + + makeAction('toggleSelected', { + type: 'geoToggleSelect', + event: 'geoselectchanged' + }); + makeAction('select', { + type: 'geoSelect', + event: 'geoselected' + }); + makeAction('unSelect', { + type: 'geoUnSelect', + event: 'geounselected' + }); + + +/***/ }, +/* 295 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + var modelUtil = __webpack_require__(5); + var ComponentModel = __webpack_require__(19); + var Model = __webpack_require__(12); + var zrUtil = __webpack_require__(4); + + var selectableMixin = __webpack_require__(140); + + var geoCreator = __webpack_require__(163); + + var GeoModel = ComponentModel.extend({ + + type: 'geo', + + /** + * @type {module:echarts/coord/geo/Geo} + */ + coordinateSystem: null, + + layoutMode: 'box', + + init: function (option) { + ComponentModel.prototype.init.apply(this, arguments); + + // Default label emphasis `position` and `show` + modelUtil.defaultEmphasis( + option.label, ['position', 'show', 'textStyle', 'distance', 'formatter'] + ); + }, + + optionUpdated: function () { + var option = this.option; + var self = this; + + option.regions = geoCreator.getFilledRegions(option.regions, option.map); + + this._optionModelMap = zrUtil.reduce(option.regions || [], function (obj, regionOpt) { + if (regionOpt.name) { + obj[regionOpt.name] = new Model(regionOpt, self); + } + return obj; + }, {}); + + this.updateSelectedMap(option.regions); + }, + + defaultOption: { + + zlevel: 0, + + z: 0, + + show: true, + + left: 'center', + + top: 'center', + + + // width:, + // height:, + // right + // bottom + + // Aspect is width / height. Inited to be geoJson bbox aspect + // This parameter is used for scale this aspect + aspectScale: 0.75, + + ///// Layout with center and size + // If you wan't to put map in a fixed size box with right aspect ratio + // This two properties may more conveninet + // layoutCenter: [50%, 50%] + // layoutSize: 100 + + + silent: false, + + // Map type + map: '', + + // Default on center of map + center: null, + + zoom: 1, + + scaleLimit: null, + + // selectedMode: false + + label: { + normal: { + show: false, + textStyle: { + color: '#000' + } + }, + emphasis: { + show: true, + textStyle: { + color: 'rgb(100,0,0)' + } + } + }, + + itemStyle: { + normal: { + // color: 各异, + borderWidth: 0.5, + borderColor: '#444', + color: '#eee' + }, + emphasis: { // 也是选中样式 + color: 'rgba(255,215,0,0.8)' + } + }, + + regions: [] + }, + + /** + * Get model of region + * @param {string} name + * @return {module:echarts/model/Model} + */ + getRegionModel: function (name) { + return this._optionModelMap[name]; + }, + + /** + * Format label + * @param {string} name Region name + * @param {string} [status='normal'] 'normal' or 'emphasis' + * @return {string} + */ + getFormattedLabel: function (name, status) { + var formatter = this.get('label.' + status + '.formatter'); + var params = { + name: name + }; + if (typeof formatter === 'function') { + params.status = status; + return formatter(params); + } + else if (typeof formatter === 'string') { + return formatter.replace('{a}', params.seriesName); + } + }, + + setZoom: function (zoom) { + this.option.zoom = zoom; + }, + + setCenter: function (center) { + this.option.center = center; + } + }); + + zrUtil.mixin(GeoModel, selectableMixin); + + module.exports = GeoModel; + + +/***/ }, +/* 296 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var MapDraw = __webpack_require__(173); + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'geo', + + init: function (ecModel, api) { + var mapDraw = new MapDraw(api, true); + this._mapDraw = mapDraw; + + this.group.add(mapDraw.group); + }, + + render: function (geoModel, ecModel, api, payload) { + // Not render if it is an toggleSelect action from self + if (payload && payload.type === 'geoToggleSelect' + && payload.from === this.uid + ) { + return; + } + + var mapDraw = this._mapDraw; + if (geoModel.get('show')) { + mapDraw.draw(geoModel, ecModel, api, this, payload); + } + else { + this._mapDraw.group.removeAll(); + } + + this.group.silent = geoModel.get('silent'); + } + }); + + +/***/ }, +/* 297 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(298); + __webpack_require__(301); + __webpack_require__(302); + + var echarts = __webpack_require__(1); + + echarts.extendComponentView({ + type: 'single' + }); + + + +/***/ }, +/* 298 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Single coordinate system creator. + */ + + + var Single = __webpack_require__(299); + + /** + * Create single coordinate system and inject it into seriesModel. + * + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @return {Array.} + */ + function create(ecModel, api) { + var singles = []; + + ecModel.eachComponent('singleAxis', function(axisModel, idx) { + + var single = new Single(axisModel, ecModel, api); + single.name = 'single_' + idx; + single.resize(axisModel, api); + axisModel.coordinateSystem = single; + singles.push(single); + + }); + + ecModel.eachSeries(function (seriesModel) { + if (seriesModel.get('coordinateSystem') === 'singleAxis') { + var singleAxisModel = ecModel.queryComponents({ + mainType: 'singleAxis', + index: seriesModel.get('singleAxisIndex'), + id: seriesModel.get('singleAxisId') + })[0]; + seriesModel.coordinateSystem = singleAxisModel.coordinateSystem; + } + }); + + return singles; + } + + __webpack_require__(26).register('single', { + create: create, + dimensions: Single.prototype.dimensions + }); + + +/***/ }, +/* 299 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Single coordinates system. + */ + + + var SingleAxis = __webpack_require__(300); + var axisHelper = __webpack_require__(114); + var layout = __webpack_require__(21); + + /** + * Create a single coordinates system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + function Single(axisModel, ecModel, api) { + + /** + * @type {string} + * @readOnly + */ + this.dimension = 'x'; + + /** + * Add it just for draw tooltip. + * + * @type {Array.} + * @readOnly + */ + this.dimensions = ['x']; + + /** + * @private + * @type {module:echarts/coord/single/SingleAxis}. + */ + this._axis = null; + + /** + * @private + * @type {module:zrender/core/BoundingRect} + */ + this._rect; + + this._init(axisModel, ecModel, api); + + /** + * @type {module:echarts/coord/single/AxisModel} + */ + this._model = axisModel; + } + + Single.prototype = { + + type: 'singleAxis', + + constructor: Single, + + /** + * Initialize single coordinate system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + * @private + */ + _init: function (axisModel, ecModel, api) { + + var dim = this.dimension; + + var axis = new SingleAxis( + dim, + axisHelper.createScaleByModel(axisModel), + [0, 0], + axisModel.get('type'), + axisModel.get('position') + ); + + var isCategory = axis.type === 'category'; + axis.onBand = isCategory && axisModel.get('boundaryGap'); + axis.inverse = axisModel.get('inverse'); + axis.orient = axisModel.get('orient'); + + axisModel.axis = axis; + axis.model = axisModel; + this._axis = axis; + }, + + /** + * Update axis scale after data processed + * @param {module:echarts/model/Global} ecModel + * @param {module:echarts/ExtensionAPI} api + */ + update: function (ecModel, api) { + this._updateAxisFromSeries(ecModel); + }, + + /** + * Update the axis extent from series. + * + * @param {module:echarts/model/Global} ecModel + * @private + */ + _updateAxisFromSeries: function (ecModel) { + + ecModel.eachSeries(function (seriesModel) { + + var data = seriesModel.getData(); + var dim = this.dimension; + this._axis.scale.unionExtent( + data.getDataExtent(seriesModel.coordDimToDataDim(dim)) + ); + axisHelper.niceScaleExtent(this._axis, this._axis.model); + }, this); + }, + + /** + * Resize the single coordinate system. + * + * @param {module:echarts/coord/single/AxisModel} axisModel + * @param {module:echarts/ExtensionAPI} api + */ + resize: function (axisModel, api) { + this._rect = layout.getLayoutRect( + { + left: axisModel.get('left'), + top: axisModel.get('top'), + right: axisModel.get('right'), + bottom: axisModel.get('bottom'), + width: axisModel.get('width'), + height: axisModel.get('height') + }, + { + width: api.getWidth(), + height: api.getHeight() + } + ); + + this._adjustAxis(); + }, + + /** + * @return {module:zrender/core/BoundingRect} + */ + getRect: function () { + return this._rect; + }, + + /** + * @private + */ + _adjustAxis: function () { + + var rect = this._rect; + var axis = this._axis; + + var isHorizontal = axis.isHorizontal(); + var extent = isHorizontal ? [0, rect.width] : [0, rect.height]; + var idx = axis.reverse ? 1 : 0; + + axis.setExtent(extent[idx], extent[1 - idx]); + + this._updateAxisTransform(axis, isHorizontal ? rect.x : rect.y); + + }, + + /** + * @param {module:echarts/coord/single/SingleAxis} axis + * @param {number} coordBase + */ + _updateAxisTransform: function (axis, coordBase) { + + var axisExtent = axis.getExtent(); + var extentSum = axisExtent[0] + axisExtent[1]; + var isHorizontal = axis.isHorizontal(); + + axis.toGlobalCoord = isHorizontal ? + function (coord) { + return coord + coordBase; + } : + function (coord) { + return extentSum - coord + coordBase; + }; + + axis.toLocalCoord = isHorizontal ? + function (coord) { + return coord - coordBase; + } : + function (coord) { + return extentSum - coord + coordBase; + }; + }, + + /** + * Get axis. + * + * @return {module:echarts/coord/single/SingleAxis} + */ + getAxis: function () { + return this._axis; + }, + + /** + * Get axis, add it just for draw tooltip. + * + * @return {[type]} [description] + */ + getBaseAxis: function () { + return this._axis; + }, + + /** + * If contain point. + * + * @param {Array.} point + * @return {boolean} + */ + containPoint: function (point) { + var rect = this.getRect(); + var axis = this.getAxis(); + var orient = axis.orient; + if (orient === 'horizontal') { + return axis.contain(axis.toLocalCoord(point[0])) + && (point[1] >= rect.y && point[1] <= (rect.y + rect.height)); + } + else { + return axis.contain(axis.toLocalCoord(point[1])) + && (point[0] >= rect.y && point[0] <= (rect.y + rect.height)); + } + }, + + /** + * @param {Array.} point + * @return {Array.} + */ + pointToData: function (point) { + var axis = this.getAxis(); + return [axis.coordToData(axis.toLocalCoord( + point[axis.orient === 'horizontal' ? 0 : 1] + ))]; + }, + + /** + * Convert the series data to concrete point. + * + * @param {number|Array.} val + * @return {Array.} + */ + dataToPoint: function (val) { + var axis = this.getAxis(); + var rect = this.getRect(); + var pt = []; + var idx = axis.orient === 'horizontal' ? 0 : 1; + pt[idx] = axis.toGlobalCoord(axis.dataToCoord(+val)); + pt[1 - idx] = idx === 0 ? (rect.y + rect.height / 2) : (rect.x + rect.width / 2); + return pt; + } + }; + + module.exports = Single; + + + +/***/ }, +/* 300 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisHelper = __webpack_require__(114); + + /** + * @constructor module:echarts/coord/single/SingleAxis + * @extends {module:echarts/coord/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var SingleAxis = function (dim, scale, coordExtent, axisType, position) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * Axis position + * - 'top' + * - 'bottom' + * - 'left' + * - 'right' + * @type {string} + */ + this.position = position || 'bottom'; + + /** + * Axis orient + * - 'horizontal' + * - 'vertical' + * @type {[type]} + */ + this.orient = null; + + /** + * @type {number} + */ + this._labelInterval = null; + + }; + + SingleAxis.prototype = { + + constructor: SingleAxis, + + /** + * Axis model + * @type {module:echarts/coord/single/AxisModel} + */ + model: null, + + /** + * Judge the orient of the axis. + * @return {boolean} + */ + isHorizontal: function () { + var position = this.position; + return position === 'top' || position === 'bottom'; + + }, + + /** + * Get interval of the axis label. + * @return {number} + */ + getLabelInterval: function () { + var labelInterval = this._labelInterval; + if (!labelInterval) { + var axisModel = this.model; + var labelModel = axisModel.getModel('axisLabel'); + var interval = labelModel.get('interval'); + if (!(this.type === 'category' && interval === 'auto')) { + + labelInterval = this._labelInterval = interval === 'auto' ? 0 : interval; + return labelInterval; + } + labelInterval = this._labelInterval = + axisHelper.getAxisLabelInterval( + zrUtil.map(this.scale.getTicks(), this.dataToCoord, this), + axisModel.getFormattedLabels(), + labelModel.getModel('textStyle').getFont(), + this.isHorizontal() + ); + } + return labelInterval; + }, + + /** + * Convert the local coord(processed by dataToCoord()) + * to global coord(concrete pixel coord). + * designated by module:echarts/coord/single/Single. + * @type {Function} + */ + toGlobalCoord: null, + + /** + * Convert the global coord to local coord. + * designated by module:echarts/coord/single/Single. + * @type {Function} + */ + toLocalCoord: null + + }; + + zrUtil.inherits(SingleAxis, Axis); + + module.exports = SingleAxis; + + +/***/ }, +/* 301 */ +/***/ function(module, exports, __webpack_require__) { + + + + var AxisBuilder = __webpack_require__(132); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var getInterval = AxisBuilder.getInterval; + var ifIgnoreOnTick = AxisBuilder.ifIgnoreOnTick; + + var axisBuilderAttrs = [ + 'axisLine', 'axisLabel', 'axisTick', 'axisName' + ]; + + var selfBuilderAttr = 'splitLine'; + + var AxisView = __webpack_require__(1).extendComponentView({ + + type: 'singleAxis', + + render: function (axisModel, ecModel) { + + var group = this.group; + + group.removeAll(); + + var layout = axisLayout(axisModel); + + var axisBuilder = new AxisBuilder(axisModel, layout); + + zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder); + + group.add(axisBuilder.getGroup()); + + if (axisModel.get(selfBuilderAttr + '.show')) { + this['_' + selfBuilderAttr](axisModel, layout.labelInterval); + } + }, + + _splitLine: function(axisModel, labelInterval) { + var axis = axisModel.axis; + var splitLineModel = axisModel.getModel('splitLine'); + var lineStyleModel = splitLineModel.getModel('lineStyle'); + var lineWidth = lineStyleModel.get('width'); + var lineColors = lineStyleModel.get('color'); + var lineInterval = getInterval(splitLineModel, labelInterval); + + lineColors = lineColors instanceof Array ? lineColors : [lineColors]; + + var gridRect = axisModel.coordinateSystem.getRect(); + var isHorizontal = axis.isHorizontal(); + + var splitLines = []; + var lineCount = 0; + + var ticksCoords = axis.getTicksCoords(); + + var p1 = []; + var p2 = []; + + for (var i = 0; i < ticksCoords.length; ++i) { + if (ifIgnoreOnTick(axis, i, lineInterval)) { + continue; + } + var tickCoord = axis.toGlobalCoord(ticksCoords[i]); + if (isHorizontal) { + p1[0] = tickCoord; + p1[1] = gridRect.y; + p2[0] = tickCoord; + p2[1] = gridRect.y + gridRect.height; + } + else { + p1[0] = gridRect.x; + p1[1] = tickCoord; + p2[0] = gridRect.x + gridRect.width; + p2[1] = tickCoord; + } + var colorIndex = (lineCount++) % lineColors.length; + splitLines[colorIndex] = splitLines[colorIndex] || []; + splitLines[colorIndex].push(new graphic.Line( + graphic.subPixelOptimizeLine({ + shape: { + x1: p1[0], + y1: p1[1], + x2: p2[0], + y2: p2[1] + }, + style: { + lineWidth: lineWidth + }, + silent: true + }))); + } + + for (var i = 0; i < splitLines.length; ++i) { + this.group.add(graphic.mergePath(splitLines[i], { + style: { + stroke: lineColors[i % lineColors.length], + lineDash: lineStyleModel.getLineDash(), + lineWidth: lineWidth + }, + silent: true + })); + } + } + }); + + function axisLayout(axisModel) { + + var single = axisModel.coordinateSystem; + var axis = axisModel.axis; + var layout = {}; + + var axisPosition = axis.position; + var orient = axis.orient; + + var rect = single.getRect(); + var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; + + var positionMap = { + horizontal: {top: rectBound[2], bottom: rectBound[3]}, + vertical: {left: rectBound[0], right: rectBound[1]} + }; + + layout.position = [ + orient === 'vertical' + ? positionMap.vertical[axisPosition] + : rectBound[0], + orient === 'horizontal' + ? positionMap.horizontal[axisPosition] + : rectBound[3] + ]; + + var r = {horizontal: 0, vertical: 1}; + layout.rotation = Math.PI / 2 * r[orient]; + + var directionMap = {top: -1, bottom: 1, right: 1, left: -1}; + + layout.labelDirection = layout.tickDirection + = layout.nameDirection + = directionMap[axisPosition]; + + if (axisModel.getModel('axisTick').get('inside')) { + layout.tickDirection = -layout.tickDirection; + } + + if (axisModel.getModel('axisLabel').get('inside')) { + layout.labelDirection = -layout.labelDirection; + } + + var labelRotation = axisModel.getModel('axisLabel').get('rotate'); + layout.labelRotation = axisPosition === 'top' ? -labelRotation : labelRotation; + + layout.labelInterval = axis.getLabelInterval(); + + layout.z2 = 1; + + return layout; + } + + module.exports = AxisView; + + + +/***/ }, +/* 302 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentModel = __webpack_require__(19); + var axisModelCreator = __webpack_require__(127); + var zrUtil = __webpack_require__(4); + + var AxisModel = ComponentModel.extend({ + + type: 'singleAxis', + + layoutMode: 'box', + + /** + * @type {module:echarts/coord/single/SingleAxis} + */ + axis: null, + + /** + * @type {module:echarts/coord/single/Single} + */ + coordinateSystem: null + + }); + + var defaultOption = { + + left: '5%', + top: '5%', + right: '5%', + bottom: '5%', + + type: 'value', + + position: 'bottom', + + orient: 'horizontal', + + axisLine: { + show: true, + lineStyle: { + width: 2, + type: 'solid' + } + }, + + axisTick: { + show: true, + length: 6, + lineStyle: { + width: 2 + } + }, + + axisLabel: { + show: true, + interval: 'auto' + }, + + splitLine: { + show: true, + lineStyle: { + type: 'dashed', + opacity: 0.2 + } + } + }; + + function getAxisType(axisName, option) { + return option.type || (option.data ? 'category' : 'value'); + } + + zrUtil.merge(AxisModel.prototype, __webpack_require__(129)); + + axisModelCreator('single', AxisModel, getAxisType, defaultOption); + + module.exports = AxisModel; + + +/***/ }, +/* 303 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * Brush component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(304) + ); + + __webpack_require__(305); + __webpack_require__(310); + __webpack_require__(311); + __webpack_require__(312); + + __webpack_require__(313); + + + +/***/ }, +/* 304 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file brush preprocessor + */ + + + var zrUtil = __webpack_require__(4); + + var DEFAULT_TOOLBOX_BTNS = ['rect', 'polygon', 'keep', 'clear']; + + module.exports = function (option, isNew) { + var brushComponents = option && option.brush; + if (!zrUtil.isArray(brushComponents)) { + brushComponents = brushComponents ? [brushComponents] : []; + } + + if (!brushComponents.length) { + return; + } + + var brushComponentSpecifiedBtns = []; + + zrUtil.each(brushComponents, function (brushOpt) { + var tbs = brushOpt.hasOwnProperty('toolbox') + ? brushOpt.toolbox : []; + + if (tbs instanceof Array) { + brushComponentSpecifiedBtns = brushComponentSpecifiedBtns.concat(tbs); + } + }); + + var toolbox = option && option.toolbox; + + if (zrUtil.isArray(toolbox)) { + toolbox = toolbox[0]; + } + if (!toolbox) { + toolbox = {feature: {}}; + option.toolbox = [toolbox]; + } + + var toolboxFeature = (toolbox.feature || (toolbox.feature = {})); + var toolboxBrush = toolboxFeature.brush || (toolboxFeature.brush = {}); + var brushTypes = toolboxBrush.type || (toolboxBrush.type = []); + + brushTypes.push.apply(brushTypes, brushComponentSpecifiedBtns); + + removeDuplicate(brushTypes); + + if (isNew && !brushTypes.length) { + brushTypes.push.apply(brushTypes, DEFAULT_TOOLBOX_BTNS); + } + }; + + function removeDuplicate(arr) { + var map = {}; + zrUtil.each(arr, function (val) { + map[val] = 1; + }); + arr.length = 0; + zrUtil.each(map, function (flag, val) { + arr.push(val); + }); + } + + + +/***/ }, +/* 305 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush visual coding. + */ + + + var echarts = __webpack_require__(1); + var visualSolution = __webpack_require__(306); + var zrUtil = __webpack_require__(4); + var BoundingRect = __webpack_require__(9); + var selector = __webpack_require__(307); + var throttle = __webpack_require__(308); + var brushHelper = __webpack_require__(309); + + var STATE_LIST = ['inBrush', 'outOfBrush']; + var DISPATCH_METHOD = '__ecBrushSelect'; + var DISPATCH_FLAG = '__ecInBrushSelectEvent'; + var PRIORITY_BRUSH = echarts.PRIORITY.VISUAL.BRUSH; + + /** + * Layout for visual, the priority higher than other layout, and before brush visual. + */ + echarts.registerLayout(PRIORITY_BRUSH, function (ecModel, api, payload) { + ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { + + payload && payload.type === 'takeGlobalCursor' && brushModel.setBrushOption( + payload.key === 'brush' ? payload.brushOption : {brushType: false} + ); + + brushModel.coordInfoList = brushHelper.makeCoordInfoList(brushModel.option, ecModel); + + brushHelper.parseInputRanges(brushModel, ecModel); + }); + }); + + /** + * Register the visual encoding if this modules required. + */ + echarts.registerVisual(PRIORITY_BRUSH, function (ecModel, api, payload) { + + var brushSelected = []; + var throttleType; + var throttleDelay; + + ecModel.eachComponent({mainType: 'brush'}, function (brushModel, brushIndex) { + + var thisBrushSelected = { + brushId: brushModel.id, + brushIndex: brushIndex, + brushName: brushModel.name, + areas: zrUtil.clone(brushModel.areas), + selected: [] + }; + // Every brush component exists in event params, convenient + // for user to find by index. + brushSelected.push(thisBrushSelected); + + var brushOption = brushModel.option; + var brushLink = brushOption.brushLink; + var linkedSeriesMap = []; + var selectedDataIndexForLink = []; + var rangeInfoBySeries = []; + var hasBrushExists = 0; + + if (!brushIndex) { // Only the first throttle setting works. + throttleType = brushOption.throttleType; + throttleDelay = brushOption.throttleDelay; + } + + // Add boundingRect and selectors to range. + var areas = zrUtil.map(brushModel.areas, function (area) { + return bindSelector( + zrUtil.defaults( + {boundingRect: boundingRectBuilders[area.brushType](area)}, + area + ) + ); + }); + + var visualMappings = visualSolution.createVisualMappings( + brushModel.option, STATE_LIST, function (mappingOption) { + mappingOption.mappingMethod = 'fixed'; + } + ); + + zrUtil.isArray(brushLink) && zrUtil.each(brushLink, function (seriesIndex) { + linkedSeriesMap[seriesIndex] = 1; + }); + + function linkOthers(seriesIndex) { + return brushLink === 'all' || linkedSeriesMap[seriesIndex]; + } + + // If no supported brush or no brush on the series, + // all visuals should be in original state. + function brushed(rangeInfoList) { + return !!rangeInfoList.length; + } + + /** + * Logic for each series: (If the logic has to be modified one day, do it carefully!) + * + * ( brushed ┬ && ┬hasBrushExist ┬ && linkOthers ) => StepA: ┬record, ┬ StepB: ┬visualByRecord. + * !brushed┘ ├hasBrushExist ┤ └nothing,┘ ├visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( !brushed && ┬hasBrushExist ┬ && linkOthers ) => StepA: nothing, StepB: ┬visualByRecord. + * └!hasBrushExist┘ └nothing. + * ( brushed ┬ && !linkOthers ) => StepA: nothing, StepB: ┬visualByCheck. + * !brushed┘ └nothing. + * ( !brushed && !linkOthers ) => StepA: nothing, StepB: nothing. + */ + + // Step A + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var rangeInfoList = rangeInfoBySeries[seriesIndex] = []; + + seriesModel.subType === 'parallel' + ? stepAParallel(seriesModel, seriesIndex, rangeInfoList) + : stepAOthers(seriesModel, seriesIndex, rangeInfoList); + }); + + function stepAParallel(seriesModel, seriesIndex) { + var coordSys = seriesModel.coordinateSystem; + hasBrushExists |= coordSys.hasAxisbrushed(); + + linkOthers(seriesIndex) && coordSys.eachActiveState( + seriesModel.getData(), + function (activeState, dataIndex) { + activeState === 'active' && (selectedDataIndexForLink[dataIndex] = 1); + } + ); + } + + function stepAOthers(seriesModel, seriesIndex, rangeInfoList) { + var selectorsByBrushType = getSelectorsByBrushType(seriesModel); + if (!selectorsByBrushType || brushModelNotControll(brushModel, seriesIndex)) { + return; + } + + zrUtil.each(areas, function (area) { + selectorsByBrushType[area.brushType] + && brushHelper.controlSeries(area, brushModel, seriesModel) + && rangeInfoList.push(area); + hasBrushExists |= brushed(rangeInfoList); + }); + + if (linkOthers(seriesIndex) && brushed(rangeInfoList)) { + var data = seriesModel.getData(); + data.each(function (dataIndex) { + if (checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex)) { + selectedDataIndexForLink[dataIndex] = 1; + } + }); + } + } + + // Step B + ecModel.eachSeries(function (seriesModel, seriesIndex) { + var seriesBrushSelected = { + seriesId: seriesModel.id, + seriesIndex: seriesIndex, + seriesName: seriesModel.name, + dataIndex: [] + }; + // Every series exists in event params, convenient + // for user to find series by seriesIndex. + thisBrushSelected.selected.push(seriesBrushSelected); + + var selectorsByBrushType = getSelectorsByBrushType(seriesModel); + var rangeInfoList = rangeInfoBySeries[seriesIndex]; + + var data = seriesModel.getData(); + var getValueState = linkOthers(seriesIndex) + ? function (dataIndex) { + return selectedDataIndexForLink[dataIndex] + ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') + : 'outOfBrush'; + } + : function (dataIndex) { + return checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) + ? (seriesBrushSelected.dataIndex.push(data.getRawIndex(dataIndex)), 'inBrush') + : 'outOfBrush'; + }; + + // If no supported brush or no brush, all visuals are in original state. + (linkOthers(seriesIndex) ? hasBrushExists : brushed(rangeInfoList)) + && visualSolution.applyVisual( + STATE_LIST, visualMappings, data, getValueState + ); + }); + + }); + + dispatchAction(api, throttleType, throttleDelay, brushSelected, payload); + }); + + function dispatchAction(api, throttleType, throttleDelay, brushSelected, payload) { + // This event will not be triggered when `setOpion`, otherwise dead lock may + // triggered when do `setOption` in event listener, which we do not find + // satisfactory way to solve yet. Some considered resolutions: + // (a) Diff with prevoius selected data ant only trigger event when changed. + // But store previous data and diff precisely (i.e., not only by dataIndex, but + // also detect value changes in selected data) might bring complexity or fragility. + // (b) Use spectial param like `silent` to suppress event triggering. + // But such kind of volatile param may be weird in `setOption`. + if (!payload) { + return; + } + + var zr = api.getZr(); + if (zr[DISPATCH_FLAG]) { + return; + } + + if (!zr[DISPATCH_METHOD]) { + zr[DISPATCH_METHOD] = doDispatch; + } + + var fn = throttle.createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType); + + fn(api, brushSelected); + } + + function doDispatch(api, brushSelected) { + if (!api.isDisposed()) { + var zr = api.getZr(); + zr[DISPATCH_FLAG] = true; + api.dispatchAction({ + type: 'brushSelect', + batch: brushSelected + }); + zr[DISPATCH_FLAG] = false; + } + } + + function checkInRange(selectorsByBrushType, rangeInfoList, data, dataIndex) { + var itemLayout = data.getItemLayout(dataIndex); + for (var i = 0, len = rangeInfoList.length; i < len; i++) { + var area = rangeInfoList[i]; + if (selectorsByBrushType[area.brushType]( + itemLayout, area.selectors, area + )) { + return true; + } + } + } + + function getSelectorsByBrushType(seriesModel) { + var brushSelector = seriesModel.brushSelector; + if (zrUtil.isString(brushSelector)) { + var sels = []; + zrUtil.each(selector, function (selectorsByElementType, brushType) { + sels[brushType] = selectorsByElementType[brushSelector]; + }); + return sels; + } + else if (zrUtil.isFunction(brushSelector)) { + var bSelector = {}; + zrUtil.each(selector, function (sel, brushType) { + bSelector[brushType] = brushSelector; + }); + return bSelector; + } + return brushSelector; + } + + function brushModelNotControll(brushModel, seriesIndex) { + var seriesIndices = brushModel.option.seriesIndex; + return seriesIndices != null + && seriesIndices !== 'all' + && ( + zrUtil.isArray(seriesIndices) + ? zrUtil.indexOf(seriesIndices, seriesIndex) < 0 + : seriesIndex !== seriesIndices + ); + } + + function bindSelector(area) { + var selectors = area.selectors = {}; + zrUtil.each(selector[area.brushType], function (selFn, elType) { + // Do not use function binding or curry for performance. + selectors[elType] = function (itemLayout) { + return selFn(itemLayout, selectors, area); + }; + }); + return area; + } + + var boundingRectBuilders = { + + lineX: zrUtil.noop, + + lineY: zrUtil.noop, + + rect: function (area) { + return getBoundingRectFromMinMax(area.range); + }, + + polygon: function (area) { + var minMax; + var range = area.range; + + for (var i = 0, len = range.length; i < len; i++) { + minMax = minMax || [[Infinity, -Infinity], [Infinity, -Infinity]]; + var rg = range[i]; + rg[0] < minMax[0][0] && (minMax[0][0] = rg[0]); + rg[0] > minMax[0][1] && (minMax[0][1] = rg[0]); + rg[1] < minMax[1][0] && (minMax[1][0] = rg[1]); + rg[1] > minMax[1][1] && (minMax[1][1] = rg[1]); + } + + return minMax && getBoundingRectFromMinMax(minMax); + } + }; + + function getBoundingRectFromMinMax(minMax) { + return new BoundingRect( + minMax[0][0], + minMax[1][0], + minMax[0][1] - minMax[0][0], + minMax[1][1] - minMax[1][0] + ); + } + + + +/***/ }, +/* 306 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual solution, for consistent option specification. + */ + + + var zrUtil = __webpack_require__(4); + var VisualMapping = __webpack_require__(192); + var each = zrUtil.each; + + function hasKeys(obj) { + if (obj) { + for (var name in obj){ + if (obj.hasOwnProperty(name)) { + return true; + } + } + } + } + + var visualSolution = { + + /** + * @param {Object} option + * @param {Array.} stateList + * @param {Function} [supplementVisualOption] + * @return {Object} visualMappings > + */ + createVisualMappings: function (option, stateList, supplementVisualOption) { + var visualMappings = {}; + + each(stateList, function (state) { + var mappings = visualMappings[state] = createMappings(); + + each(option[state], function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + var mappingOption = { + type: visualType, + visual: visualData + }; + supplementVisualOption && supplementVisualOption(mappingOption, state); + mappings[visualType] = new VisualMapping(mappingOption); + + // Prepare a alpha for opacity, for some case that opacity + // is not supported, such as rendering using gradient color. + if (visualType === 'opacity') { + mappingOption = zrUtil.clone(mappingOption); + mappingOption.type = 'colorAlpha'; + mappings.__hidden.__alphaForOpacity = new VisualMapping(mappingOption); + } + }); + }); + + return visualMappings; + + function createMappings() { + var Creater = function () {}; + // Make sure hidden fields will not be visited by + // object iteration (with hasOwnProperty checking). + Creater.prototype.__hidden = Creater.prototype; + var obj = new Creater(); + return obj; + } + }, + + /** + * @param {Object} thisOption + * @param {Object} newOption + * @param {Array.} keys + */ + replaceVisualOption: function (thisOption, newOption, keys) { + // Visual attributes merge is not supported, otherwise it + // brings overcomplicated merge logic. See #2853. So if + // newOption has anyone of these keys, all of these keys + // will be reset. Otherwise, all keys remain. + var has; + zrUtil.each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + has = true; + } + }); + has && zrUtil.each(keys, function (key) { + if (newOption.hasOwnProperty(key) && hasKeys(newOption[key])) { + thisOption[key] = zrUtil.clone(newOption[key]); + } + else { + delete thisOption[key]; + } + }); + }, + + /** + * @param {Array.} stateList + * @param {Object} visualMappings > + * @param {module:echarts/data/List} list + * @param {Function} getValueState param: valueOrIndex, return: state. + * @param {object} [scope] Scope for getValueState + * @param {string} [dimension] Concrete dimension, if used. + */ + applyVisual: function (stateList, visualMappings, data, getValueState, scope, dimension) { + var visualTypesMap = {}; + zrUtil.each(stateList, function (state) { + var visualTypes = VisualMapping.prepareVisualTypes(visualMappings[state]); + visualTypesMap[state] = visualTypes; + }); + + var dataIndex; + + function getVisual(key) { + return data.getItemVisual(dataIndex, key); + } + + function setVisual(key, value) { + data.setItemVisual(dataIndex, key, value); + } + + if (dimension == null) { + data.each(eachItem, true); + } + else { + data.each([dimension], eachItem, true); + } + + function eachItem(valueOrIndex, index) { + dataIndex = dimension == null ? valueOrIndex : index; + var valueState = getValueState.call(scope, valueOrIndex); + var mappings = visualMappings[valueState]; + var visualTypes = visualTypesMap[valueState]; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + mappings[type] && mappings[type].applyVisual( + valueOrIndex, getVisual, setVisual + ); + } + } + } + }; + + module.exports = visualSolution; + + + +/***/ }, +/* 307 */ +/***/ function(module, exports, __webpack_require__) { + + + + var polygonContain = __webpack_require__(167).contain; + var BoundingRect = __webpack_require__(9); + + // Key of the first level is brushType: `line`, `rect`, `polygon`. + // Key of the second level is chart element type: `point`, `rect`. + // See moudule:echarts/component/helper/BrushController + // function param: + // {Object} itemLayout fetch from data.getItemLayout(dataIndex) + // {Object} selectors {point: selector, rect: selector, ...} + // {Object} area {range: [[], [], ..], boudingRect} + // function return: + // {boolean} Whether in the given brush. + var selector = { + lineX: getLineSelectors(0), + lineY: getLineSelectors(1), + rect: { + point: function (itemLayout, selectors, area) { + return area.boundingRect.contain(itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + return area.boundingRect.intersect(makeBoundingRect(itemLayout)); + } + }, + polygon: { + point: function (itemLayout, selectors, area) { + return area.boundingRect.contain(itemLayout[0], itemLayout[1]) + && polygonContain(area.range, itemLayout[0], itemLayout[1]); + }, + rect: function (itemLayout, selectors, area) { + // FIXME + // 随意写的,没有考察过效率。 + var points = area.range; + + if (points.length <= 1) { + return false; + } + + var x = itemLayout.x; + var y = itemLayout.y; + var width = itemLayout.width; + var height = itemLayout.height; + var p = points[0]; + + if (polygonContain(points, x, y) + || polygonContain(points, x + width, y) + || polygonContain(points, x, y + height) + || polygonContain(points, x + width, y + height) + || makeBoundingRect(itemLayout).contain(p[0], p[1]) + || lineIntersectPolygon(x, y, x + width, y, points) + || lineIntersectPolygon(x, y, x, y + height, points) + || lineIntersectPolygon(x + width, y, x + width, y + height, points) + || lineIntersectPolygon(x, y + height, x + width, y + height, points) + ) { + return true; + } + } + } + }; + + function getLineSelectors(xyIndex) { + var xy = ['x', 'y']; + var wh = ['width', 'height']; + + return { + point: function (itemLayout, selectors, area) { + var range = area.range; + var p = itemLayout[xyIndex]; + return inLineRange(p, range); + }, + rect: function (itemLayout, selectors, area) { + var range = area.range; + return inLineRange(itemLayout[xy[xyIndex]], range) + || inLineRange(itemLayout[xy[xyIndex]] + itemLayout[wh[xyIndex]], range); + } + }; + } + + function inLineRange(p, range) { + return range[0] <= p && p <= range[1]; + } + + // FIXME + // 随意写的,没考察过效率。 + function lineIntersectPolygon(lx, ly, l2x, l2y, points) { + for (var i = 0, p2 = points[points.length - 1]; i < points.length; i++) { + var p = points[i]; + if (lineIntersect(lx, ly, l2x, l2y, p[0], p[1], p2[0], p2[1])) { + return true; + } + p2 = p; + } + } + + // Code from with some fix. + // See + function lineIntersect(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y) { + var delta = determinant(a2x - a1x, b1x - b2x, a2y - a1y, b1y - b2y); + if (nearZero(delta)) { // parallel + return false; + } + var namenda = determinant(b1x - a1x, b1x - b2x, b1y - a1y, b1y - b2y) / delta; + if (namenda < 0 || namenda > 1) { + return false; + } + var miu = determinant(a2x - a1x, b1x - a1x, a2y - a1y, b1y - a1y) / delta; + if (miu < 0 || miu > 1) { + return false; + } + return true; + } + + function nearZero(val) { + return val <= (1e-6) && val >= -(1e-6); + } + + function determinant(v1, v2, v3, v4) { + return v1 * v4 - v2 * v3; + } + + function makeBoundingRect(itemLayout) { + var x = itemLayout.x; + var y = itemLayout.y; + var width = itemLayout.width; + var height = itemLayout.height; + + // width and height might be negative. + if (width < 0) { + x = x + width; + width = -width; + } + if (height < 0) { + y = y + height; + height = -height; + } + return new BoundingRect(x, y, width, height); + } + + module.exports = selector; + + + +/***/ }, +/* 308 */ +/***/ function(module, exports) { + + + + var lib = {}; + + var ORIGIN_METHOD = '\0__throttleOriginMethod'; + var RATE = '\0__throttleRate'; + var THROTTLE_TYPE = '\0__throttleType'; + + /** + * @public + * @param {(Function)} fn + * @param {number} [delay=0] Unit: ms. + * @param {boolean} [debounce=false] + * true: If call interval less than `delay`, only the last call works. + * false: If call interval less than `delay, call works on fixed rate. + * @return {(Function)} throttled fn. + */ + lib.throttle = function (fn, delay, debounce) { + + var currCall; + var lastCall = 0; + var lastExec = 0; + var timer = null; + var diff; + var scope; + var args; + + delay = delay || 0; + + function exec() { + lastExec = (new Date()).getTime(); + timer = null; + fn.apply(scope, args || []); + } + + var cb = function () { + currCall = (new Date()).getTime(); + scope = this; + args = arguments; + diff = currCall - (debounce ? lastCall : lastExec) - delay; + + clearTimeout(timer); + + if (debounce) { + timer = setTimeout(exec, delay); + } + else { + if (diff >= 0) { + exec(); + } + else { + timer = setTimeout(exec, -diff); + } + } + + lastCall = currCall; + }; + + /** + * Clear throttle. + * @public + */ + cb.clear = function () { + if (timer) { + clearTimeout(timer); + timer = null; + } + }; + + return cb; + }; + + /** + * Create throttle method or update throttle rate. + * + * @example + * ComponentView.prototype.render = function () { + * ... + * throttle.createOrUpdate( + * this, + * '_dispatchAction', + * this.model.get('throttle'), + * 'fixRate' + * ); + * }; + * ComponentView.prototype.remove = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * ComponentView.prototype.dispose = function () { + * throttle.clear(this, '_dispatchAction'); + * }; + * + * @public + * @param {Object} obj + * @param {string} fnAttr + * @param {number} [rate] + * @param {string} [throttleType='fixRate'] 'fixRate' or 'debounce' + * @return {Function} throttled function. + */ + lib.createOrUpdate = function (obj, fnAttr, rate, throttleType) { + var fn = obj[fnAttr]; + + if (!fn) { + return; + } + + var originFn = fn[ORIGIN_METHOD] || fn; + var lastThrottleType = fn[THROTTLE_TYPE]; + var lastRate = fn[RATE]; + + if (lastRate !== rate || lastThrottleType !== throttleType) { + if (rate == null || !throttleType) { + return (obj[fnAttr] = originFn); + } + + fn = obj[fnAttr] = lib.throttle( + originFn, rate, throttleType === 'debounce' + ); + fn[ORIGIN_METHOD] = originFn; + fn[THROTTLE_TYPE] = throttleType; + fn[RATE] = rate; + } + + return fn; + }; + + /** + * Clear throttle. Example see throttle.createOrUpdate. + * + * @public + * @param {Object} obj + * @param {string} fnAttr + */ + lib.clear = function (obj, fnAttr) { + var fn = obj[fnAttr]; + if (fn && fn[ORIGIN_METHOD]) { + obj[fnAttr] = fn[ORIGIN_METHOD]; + } + }; + + module.exports = lib; + + + +/***/ }, +/* 309 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + + var each = zrUtil.each; + + var helper = {}; + + var COMPONENT_NAMES = ['geo', 'xAxis', 'yAxis']; + var PANEL_ID_SPLIT = '--'; + var COORD_CONVERTS = ['dataToPoint', 'pointToData']; + + helper.parseOutputRanges = function (areas, coordInfoList, ecModel, rangesCoordInfo) { + each(areas, function (area, index) { + var panelId = area.panelId; + + if (panelId) { + panelId = panelId.split(PANEL_ID_SPLIT); + + area[panelId[0] + 'Index'] = +panelId[1]; + + var coordInfo = findCoordInfo(area, coordInfoList); + area.coordRange = coordConvert[area.brushType]( + 1, coordInfo, area.range + ); + rangesCoordInfo && (rangesCoordInfo[index] = coordInfo); + } + }); + }; + + helper.parseInputRanges = function (brushModel, ecModel) { + each(brushModel.areas, function (area) { + var coordInfo = findCoordInfo(area, brushModel.coordInfoList); + + if (true) { + zrUtil.assert( + !coordInfo || coordInfo === true || area.coordRange, + 'coordRange must be specified when coord index specified.' + ); + zrUtil.assert( + !coordInfo || coordInfo !== true || area.range, + 'range must be specified.' + ); + } + + area.range = area.range || []; + + // convert coordRange to global range and set panelId. + if (coordInfo && coordInfo !== true) { + area.range = coordConvert[area.brushType]( + 0, coordInfo, area.coordRange + ); + area.panelId = coordInfo.panelId; + } + }); + }; + + helper.makePanelOpts = function (coordInfoList) { + var panelOpts = []; + + each(coordInfoList, function (coordInfo) { + var coordSys = coordInfo.coordSys; + var rect; + + if (coordInfo.geoIndex >= 0) { + rect = coordSys.getBoundingRect().clone(); + // geo roam and zoom transform + rect.applyTransform(graphic.getTransform(coordSys)); + } + else { // xAxis or yAxis + // grid is not Transformable. + rect = coordSys.grid.getRect().clone(); + } + + panelOpts.push({panelId: coordInfo.panelId, rect: rect}); + }); + + return panelOpts; + }; + + /** + * @param {Object} option {xAxisIndex, yAxisIndex, geoIndex} + * @param {module:echarts/model/Global} ecModel + * @return {Array.} coordInfoList + */ + helper.makeCoordInfoList = function (option, ecModel) { + var coordInfoList = []; + + each(COMPONENT_NAMES, function (componentName) { + var componentIndices = option[componentName + 'Index']; + if (componentIndices == null || componentIndices === 'none') { + return; + } + if (componentIndices !== 'all' && !zrUtil.isArray(componentIndices)) { + componentIndices = [componentIndices]; + } + + ecModel.eachComponent({mainType: componentName}, function (componentModel, index) { + if (componentIndices !== 'all' && zrUtil.indexOf(componentIndices, index) < 0) { + return; + } + + var grid; + var coordSys; + + (componentName === 'xAxis' || componentName === 'yAxis') + ? (grid = componentModel.axis.grid) + : (coordSys = componentModel.coordinateSystem); // geo + + var coordInfo; + + // Check duplicate and find cartesian when tranval to yAxis. + for (var i = 0, len = coordInfoList.length; i < len; i++) { + var cInfo = coordInfoList[i]; + if (true) { + zrUtil.assert( + cInfo[componentName + 'Index'] != index, + 'Coord should not be defined duplicately: ' + componentName + index + ); + } + // CoordSys is always required for `rect brush` or `polygon brush`. + // If both xAxisIndex and yAxisIndex specified, fetch cartesian by them. + if (componentName === 'yAxis' && !cInfo.yAxis && cInfo.xAxis) { + var aCoordSys = grid.getCartesian(cInfo.xAxisIndex, index); + if (aCoordSys) { // The yAxis and xAxis are in the same cartesian. + coordSys = aCoordSys; + coordInfo = cInfo; + break; + } + } + } + + !coordInfo && coordInfoList.push(coordInfo = {}); + + coordInfo[componentName] = componentModel; + coordInfo[componentName + 'Index'] = index; + // If both xAxisIndex and yAxisIndex specified, panelId only use yAxisIndex, + // which is enough to index panel. + coordInfo.panelId = componentName + PANEL_ID_SPLIT + index; + coordInfo.coordSys = coordSys + // If only xAxisIndex or only yAxisIndex specified, find its first cartesian. + || grid.getCartesian(coordInfo.xAxisIndex, coordInfo.yAxisIndex); + + coordInfo.coordSys + ? (coordInfoList[componentName + 'Has'] = true) + : coordInfoList.pop(); // If a coordInfo exists originally, existance of coordSys is ensured. + }); + }); + + return coordInfoList; + }; + + helper.controlSeries = function (area, brushModel, seriesModel) { + // Check whether area is bound in coord, and series do not belong to that coord. + // If do not do this check, some brush (like lineX) will controll all axes. + var coordInfo = findCoordInfo(area, brushModel.coordInfoList); + return coordInfo === true || (coordInfo && coordInfo.coordSys === seriesModel.coordinateSystem); + }; + + function formatMinMax(minMax) { + minMax[0] > minMax[1] && minMax.reverse(); + return minMax; + } + + /** + * If return Object, a coord found. + * If reutrn true, global found. + * Otherwise nothing found. + * + * @param {Object} area {Index} + * @param {Array} coordInfoList + * @return {Obejct|boolean} + */ + function findCoordInfo(area, coordInfoList) { + var isGlobal = true; + for (var j = 0; j < COMPONENT_NAMES.length; j++) { + var indexAttr = COMPONENT_NAMES[j] + 'Index'; + if (area[indexAttr] >= 0) { + isGlobal = false; + for (var i = 0; i < coordInfoList.length; i++) { + if (coordInfoList[i][indexAttr] === area[indexAttr]) { + return coordInfoList[i]; + } + } + } + } + return isGlobal; + } + + function axisConvert(axisName, to, coordInfo, coordRange) { + var axis = coordInfo.coordSys.getAxis(axisName); + + if (true) { + zrUtil.assert(axis, 'line brush is only available in cartesian (grid).'); + } + + return formatMinMax(zrUtil.map([0, 1], function (i) { + return to + ? axis.coordToData(axis.toLocalCoord(coordRange[i])) + : axis.toGlobalCoord(axis.dataToCoord(coordRange[i])); + })); + } + + var coordConvert = { + + lineX: zrUtil.curry(axisConvert, 'x'), + + lineY: zrUtil.curry(axisConvert, 'y'), + + rect: function (to, coordInfo, coordRange) { + var coordSys = coordInfo.coordSys; + var xminymin = coordSys[COORD_CONVERTS[to]]([coordRange[0][0], coordRange[1][0]]); + var xmaxymax = coordSys[COORD_CONVERTS[to]]([coordRange[0][1], coordRange[1][1]]); + return [ + formatMinMax([xminymin[0], xmaxymax[0]]), + formatMinMax([xminymin[1], xmaxymax[1]]) + ]; + }, + + polygon: function (to, coordInfo, coordRange) { + var coordSys = coordInfo.coordSys; + return zrUtil.map(coordRange, coordSys[COORD_CONVERTS[to]], coordSys); + } + }; + + module.exports = helper; + + + +/***/ }, +/* 310 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush model + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + var visualSolution = __webpack_require__(306); + var Model = __webpack_require__(12); + + var DEFAULT_OUT_OF_BRUSH_COLOR = ['#ddd']; + + var BrushModel = echarts.extendComponentModel({ + + type: 'brush', + + dependencies: ['geo', 'grid', 'xAxis', 'yAxis', 'parallel', 'series'], + + /** + * @protected + */ + defaultOption: { + // inBrush: null, + // outOfBrush: null, + toolbox: null, // Default value see preprocessor. + brushLink: null, // Series indices array, broadcast using dataIndex. + // or 'all', which means all series. 'none' or null means no series. + seriesIndex: 'all', // seriesIndex array, specify series controlled by this brush component. + geoIndex: null, // + xAxisIndex: null, + yAxisIndex: null, + + brushType: 'rect', // Default brushType, see BrushController. + brushMode: 'single', // Default brushMode, 'single' or 'multiple' + transformable: true, // Default transformable. + brushStyle: { // Default brushStyle + borderWidth: 1, + color: 'rgba(120,140,180,0.3)', + borderColor: 'rgba(120,140,180,0.8)', + width: null // do not use bursh width in line brush, but fetch from grid. + }, + + throttleType: 'fixRate',// Throttle in brushSelected event. 'fixRate' or 'debounce'. + // If null, no throttle. Valid only in the first brush component + throttleDelay: 0, // Unit: ms, 0 means every event will be triggered. + + // FIXME + // 试验效果 + removeOnClick: true + }, + + /** + * @readOnly + * @type {Array.} + */ + areas: [], + + /** + * Current activated brush type. + * If null, brush is inactived. + * see module:echarts/component/helper/BrushController + * @readOnly + * @type {string} + */ + brushType: null, + + /** + * Current brush opt. + * see module:echarts/component/helper/BrushController + * @readOnly + * @type {Object} + */ + brushOption: {}, + + /** + * @readOnly + * @type {Array.} + */ + coordInfoList: [], + + optionUpdated: function (newOption, isInit) { + var thisOption = this.option; + + !isInit && visualSolution.replaceVisualOption( + thisOption, newOption, ['inBrush', 'outOfBrush'] + ); + + thisOption.inBrush = thisOption.inBrush || {}; + // Always give default visual, consider setOption at the second time. + thisOption.outOfBrush = thisOption.outOfBrush || {color: DEFAULT_OUT_OF_BRUSH_COLOR}; + }, + + /** + * If ranges is null/undefined, range state remain. + * + * @param {Array.} [ranges] + */ + setAreas: function (areas) { + if (true) { + zrUtil.assert(zrUtil.isArray(areas)); + zrUtil.each(areas, function (area) { + zrUtil.assert(area.brushType, 'Illegal areas'); + }); + } + + // If ranges is null/undefined, range state remain. + // This helps user to dispatchAction({type: 'brush'}) with no areas + // set but just want to get the current brush select info from a `brush` event. + if (!areas) { + return; + } + + this.areas = zrUtil.map(areas, function (area) { + return this._mergeBrushOption(area); + }, this); + }, + + /** + * see module:echarts/component/helper/BrushController + * @param {Object} brushOption + */ + setBrushOption: function (brushOption) { + this.brushOption = this._mergeBrushOption(brushOption); + this.brushType = this.brushOption.brushType; + }, + + /** + * @private + */ + _mergeBrushOption: function (brushOption) { + var option = this.option; + return zrUtil.merge( + { + brushType: option.brushType, + brushMode: option.brushMode, + transformable: option.transformable, + brushStyle: new Model(option.brushStyle).getItemStyle(), + removeOnClick: option.removeOnClick + }, + brushOption, + true + ); + } + + }); + + module.exports = BrushModel; + + + +/***/ }, +/* 311 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var BrushController = __webpack_require__(233); + var echarts = __webpack_require__(1); + var brushHelper = __webpack_require__(309); + + module.exports = echarts.extendComponentView({ + + type: 'brush', + + init: function (ecModel, api) { + + /** + * @readOnly + * @type {module:echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @readOnly + * @type {module:echarts/ExtensionAPI} + */ + this.api = api; + + /** + * @readOnly + * @type {module:echarts/component/brush/BrushModel} + */ + this.model; + + /** + * @private + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)) + .mount(); + }, + + /** + * @override + */ + render: function (brushModel) { + this.model = brushModel; + return updateController.apply(this, arguments); + }, + + /** + * @override + */ + updateView: updateController, + + /** + * @override + */ + updateLayout: updateController, + + /** + * @override + */ + updateVisual: updateController, + + /** + * @override + */ + dispose: function () { + this._brushController.dispose(); + }, + + /** + * @private + */ + _onBrush: function (areas, opt) { + var modelId = this.model.id; + + brushHelper.parseOutputRanges(areas, this.model.coordInfoList, this.ecModel); + + // Action is not dispatched on drag end, because the drag end + // emits the same params with the last drag move event, and + // may have some delay when using touch pad, which makes + // animation not smooth (when using debounce). + (!opt.isEnd || opt.removeOnClick) && this.api.dispatchAction({ + type: 'brush', + brushId: modelId, + areas: zrUtil.clone(areas), + $from: modelId + }); + } + + }); + + function updateController(brushModel, ecModel, api, payload) { + // Do not update controller when drawing. + (!payload || payload.$from !== brushModel.id) && this._brushController + .setPanels(brushHelper.makePanelOpts(brushModel.coordInfoList)) + .enableBrush(brushModel.brushOption) + .updateCovers(brushModel.areas.slice()); + } + + + +/***/ }, +/* 312 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Brush action + */ + + + var echarts = __webpack_require__(1); + + /** + * payload: { + * brushIndex: number, or, + * brushId: string, or, + * brushName: string, + * globalRanges: Array + * } + */ + echarts.registerAction( + {type: 'brush', event: 'brush', update: 'updateView'}, + function (payload, ecModel) { + ecModel.eachComponent({mainType: 'brush', query: payload}, function (brushModel) { + brushModel.setAreas(payload.areas); + }); + } + ); + + /** + * payload: { + * brushComponents: [ + * { + * brushId, + * brushIndex, + * brushName, + * series: [ + * { + * seriesId, + * seriesIndex, + * seriesName, + * rawIndices: [21, 34, ...] + * }, + * ... + * ] + * }, + * ... + * ] + * } + */ + echarts.registerAction( + {type: 'brushSelect', event: 'brushSelected', update: 'none'}, + function () {} + ); + + +/***/ }, +/* 313 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + + function Brush(model, ecModel, api) { + this.model = model; + this.ecModel = ecModel; + this.api = api; + + /** + * @private + * @type {string} + */ + this._brushType; + + /** + * @private + * @type {string} + */ + this._brushMode; + } + + Brush.defaultOption = { + show: true, + type: ['rect', 'polygon', 'lineX', 'lineY', 'keep', 'clear'], + icon: { + rect: 'M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13', // jshint ignore:line + polygon: 'M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2', // jshint ignore:line + lineX: 'M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4', // jshint ignore:line + lineY: 'M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4', // jshint ignore:line + keep: 'M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z', // jshint ignore:line + clear: 'M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2' // jshint ignore:line + }, + title: { + rect: '矩形选择', + polygon: '圈选', + lineX: '横向选择', + lineY: '纵向选择', + keep: '保持选择', + clear: '清除选择' + } + }; + + var proto = Brush.prototype; + + proto.render = + proto.updateView = + proto.updateLayout = function (featureModel, ecModel, api) { + var brushType; + var brushMode; + var isBrushed; + + ecModel.eachComponent({mainType: 'brush'}, function (brushModel) { + brushType = brushModel.brushType; + brushMode = brushModel.brushOption.brushMode || 'single'; + isBrushed |= brushModel.areas.length; + }); + this._brushType = brushType; + this._brushMode = brushMode; + + zrUtil.each(featureModel.get('type', true), function (type) { + featureModel.setIconStatus( + type, + ( + type === 'keep' + ? brushMode === 'multiple' + : type === 'clear' + ? isBrushed + : type === brushType + ) ? 'emphasis' : 'normal' + ); + }); + }; + + proto.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon', true); + var icons = {}; + zrUtil.each(model.get('type', true), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + + proto.onclick = function (ecModel, api, type) { + var api = this.api; + var brushType = this._brushType; + var brushMode = this._brushMode; + + if (type === 'clear') { + api.dispatchAction({ + type: 'brush', + // Clear all areas of all brush components. + areas: [] + }); + } + else { + api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'brush', + brushOption: { + brushType: type === 'keep' + ? brushType + : (brushType === type ? false : type), + brushMode: type === 'keep' + ? (brushMode === 'multiple' ? 'single' : 'multiple') + : brushMode + } + }); + } + }; + + featureManager.register('brush', Brush); + + module.exports = Brush; + + +/***/ }, +/* 314 */ +/***/ function(module, exports) { + + 'use strict'; + + + var features = {}; + + module.exports = { + register: function (name, ctor) { + features[name] = ctor; + }, + + get: function (name) { + return features[name]; + } + }; + + +/***/ }, +/* 315 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var echarts = __webpack_require__(1); + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + + // Model + echarts.extendComponentModel({ + + type: 'title', + + layoutMode: {type: 'box', ignoreSize: true}, + + defaultOption: { + // 一级层叠 + zlevel: 0, + // 二级层叠 + z: 6, + show: true, + + text: '', + // 超链接跳转 + // link: null, + // 仅支持self | blank + target: 'blank', + subtext: '', + + // 超链接跳转 + // sublink: null, + // 仅支持self | blank + subtarget: 'blank', + + // 'center' ¦ 'left' ¦ 'right' + // ¦ {number}(x坐标,单位px) + left: 0, + // 'top' ¦ 'bottom' ¦ 'center' + // ¦ {number}(y坐标,单位px) + top: 0, + + // 水平对齐 + // 'auto' | 'left' | 'right' | 'center' + // 默认根据 left 的位置判断是左对齐还是右对齐 + // textAlign: null + // + // 垂直对齐 + // 'auto' | 'top' | 'bottom' | 'middle' + // 默认根据 top 位置判断是上对齐还是下对齐 + // textBaseline: null + + backgroundColor: 'rgba(0,0,0,0)', + + // 标题边框颜色 + borderColor: '#ccc', + + // 标题边框线宽,单位px,默认为0(无边框) + borderWidth: 0, + + // 标题内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + padding: 5, + + // 主副标题纵向间隔,单位px,默认为10, + itemGap: 10, + textStyle: { + fontSize: 18, + fontWeight: 'bolder', + color: '#333' + }, + subtextStyle: { + color: '#aaa' + } + } + }); + + // View + echarts.extendComponentView({ + + type: 'title', + + render: function (titleModel, ecModel, api) { + this.group.removeAll(); + + if (!titleModel.get('show')) { + return; + } + + var group = this.group; + + var textStyleModel = titleModel.getModel('textStyle'); + var subtextStyleModel = titleModel.getModel('subtextStyle'); + + var textAlign = titleModel.get('textAlign'); + var textBaseline = titleModel.get('textBaseline'); + + var textEl = new graphic.Text({ + style: { + text: titleModel.get('text'), + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + }, + z2: 10 + }); + + var textRect = textEl.getBoundingRect(); + + var subText = titleModel.get('subtext'); + var subTextEl = new graphic.Text({ + style: { + text: subText, + textFont: subtextStyleModel.getFont(), + fill: subtextStyleModel.getTextColor(), + y: textRect.height + titleModel.get('itemGap'), + textBaseline: 'top' + }, + z2: 10 + }); + + var link = titleModel.get('link'); + var sublink = titleModel.get('sublink'); + + textEl.silent = !link; + subTextEl.silent = !sublink; + + if (link) { + textEl.on('click', function () { + window.open(link, '_' + titleModel.get('target')); + }); + } + if (sublink) { + subTextEl.on('click', function () { + window.open(sublink, '_' + titleModel.get('subtarget')); + }); + } + + group.add(textEl); + subText && group.add(subTextEl); + // If no subText, but add subTextEl, there will be an empty line. + + var groupRect = group.getBoundingRect(); + var layoutOption = titleModel.getBoxLayoutParams(); + layoutOption.width = groupRect.width; + layoutOption.height = groupRect.height; + var layoutRect = layout.getLayoutRect( + layoutOption, { + width: api.getWidth(), + height: api.getHeight() + }, titleModel.get('padding') + ); + // Adjust text align based on position + if (!textAlign) { + // Align left if title is on the left. center and right is same + textAlign = titleModel.get('left') || titleModel.get('right'); + if (textAlign === 'middle') { + textAlign = 'center'; + } + // Adjust layout by text align + if (textAlign === 'right') { + layoutRect.x += layoutRect.width; + } + else if (textAlign === 'center') { + layoutRect.x += layoutRect.width / 2; + } + } + if (!textBaseline) { + textBaseline = titleModel.get('top') || titleModel.get('bottom'); + if (textBaseline === 'center') { + textBaseline = 'middle'; + } + if (textBaseline === 'bottom') { + layoutRect.y += layoutRect.height; + } + else if (textBaseline === 'middle') { + layoutRect.y += layoutRect.height / 2; + } + + textBaseline = textBaseline || 'top'; + } + + group.attr('position', [layoutRect.x, layoutRect.y]); + var alignStyle = { + textAlign: textAlign, + textVerticalAlign: textBaseline + }; + textEl.setStyle(alignStyle); + subTextEl.setStyle(alignStyle); + + // Render background + // Get groupRect again because textAlign has been changed + groupRect = group.getBoundingRect(); + var padding = layoutRect.margin; + var style = titleModel.getItemStyle(['color', 'opacity']); + style.fill = titleModel.get('backgroundColor'); + var rect = new graphic.Rect({ + shape: { + x: groupRect.x - padding[3], + y: groupRect.y - padding[0], + width: groupRect.width + padding[1] + padding[3], + height: groupRect.height + padding[0] + padding[2] + }, + style: style, + silent: true + }); + graphic.subPixelOptimizeRect(rect); + + group.add(rect); + } + }); + + +/***/ }, +/* 316 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(317); + + __webpack_require__(318); + __webpack_require__(321); + + __webpack_require__(322); + __webpack_require__(323); + + __webpack_require__(325); + __webpack_require__(326); + + __webpack_require__(328); + __webpack_require__(329); + + + +/***/ }, +/* 317 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('dataZoom', function (option) { + // Default 'slider' when no type specified. + return 'slider'; + }); + + + +/***/ }, +/* 318 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + var echarts = __webpack_require__(1); + var modelUtil = __webpack_require__(5); + var helper = __webpack_require__(319); + var AxisProxy = __webpack_require__(320); + var each = zrUtil.each; + var eachAxisDim = helper.eachAxisDim; + + var DataZoomModel = echarts.extendComponentModel({ + + type: 'dataZoom', + + dependencies: [ + 'xAxis', 'yAxis', 'zAxis', 'radiusAxis', 'angleAxis', 'series' + ], + + /** + * @protected + */ + defaultOption: { + zlevel: 0, + z: 4, // Higher than normal component (z: 2). + orient: null, // Default auto by axisIndex. Possible value: 'horizontal', 'vertical'. + xAxisIndex: null, // Default the first horizontal category axis. + yAxisIndex: null, // Default the first vertical category axis. + + + filterMode: 'filter', // Possible values: 'filter' or 'empty'. + // 'filter': data items which are out of window will be removed. + // This option is applicable when filtering outliers. + // 'empty': data items which are out of window will be set to empty. + // This option is applicable when user should not neglect + // that there are some data items out of window. + // Taking line chart as an example, line will be broken in + // the filtered points when filterModel is set to 'empty', but + // be connected when set to 'filter'. + + throttle: null, // Dispatch action by the fixed rate, avoid frequency. + // default 100. Do not throttle when use null/undefined. + // If animation === true and animationDurationUpdate > 0, + // default value is 100, otherwise 20. + start: 0, // Start percent. 0 ~ 100 + end: 100, // End percent. 0 ~ 100 + startValue: null, // Start value. If startValue specified, start is ignored. + endValue: null // End value. If endValue specified, end is ignored. + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel) { + + /** + * key like x_0, y_1 + * @private + * @type {Object} + */ + this._dataIntervalByAxis = {}; + + /** + * @private + */ + this._dataInfo = {}; + + /** + * key like x_0, y_1 + * @private + */ + this._axisProxies = {}; + + /** + * @readOnly + */ + this.textStyleModel; + + /** + * @private + */ + this._autoThrottle = true; + + var rawOption = retrieveRaw(option); + + this.mergeDefaultAndTheme(option, ecModel); + + this.doInit(rawOption); + }, + + /** + * @override + */ + mergeOption: function (newOption) { + var rawOption = retrieveRaw(newOption); + + //FIX #2591 + zrUtil.merge(this.option, newOption, true); + + this.doInit(rawOption); + }, + + /** + * @protected + */ + doInit: function (rawOption) { + var thisOption = this.option; + + // Disable realtime view update if canvas is not supported. + if (!env.canvasSupported) { + thisOption.realtime = false; + } + + this._setDefaultThrottle(rawOption); + + processRangeProp('start', 'startValue', rawOption, thisOption); + processRangeProp('end', 'endValue', rawOption, thisOption); + + this.textStyleModel = this.getModel('textStyle'); + + this._resetTarget(); + + this._giveAxisProxies(); + }, + + /** + * @private + */ + _giveAxisProxies: function () { + var axisProxies = this._axisProxies; + + this.eachTargetAxis(function (dimNames, axisIndex, dataZoomModel, ecModel) { + var axisModel = this.dependentModels[dimNames.axis][axisIndex]; + + // If exists, share axisProxy with other dataZoomModels. + var axisProxy = axisModel.__dzAxisProxy || ( + // Use the first dataZoomModel as the main model of axisProxy. + axisModel.__dzAxisProxy = new AxisProxy( + dimNames.name, axisIndex, this, ecModel + ) + ); + // FIXME + // dispose __dzAxisProxy + + axisProxies[dimNames.name + '_' + axisIndex] = axisProxy; + }, this); + }, + + /** + * @private + */ + _resetTarget: function () { + var thisOption = this.option; + + var autoMode = this._judgeAutoMode(); + + eachAxisDim(function (dimNames) { + var axisIndexName = dimNames.axisIndex; + thisOption[axisIndexName] = modelUtil.normalizeToArray( + thisOption[axisIndexName] + ); + }, this); + + if (autoMode === 'axisIndex') { + this._autoSetAxisIndex(); + } + else if (autoMode === 'orient') { + this._autoSetOrient(); + } + }, + + /** + * @private + */ + _judgeAutoMode: function () { + // Auto set only works for setOption at the first time. + // The following is user's reponsibility. So using merged + // option is OK. + var thisOption = this.option; + + var hasIndexSpecified = false; + eachAxisDim(function (dimNames) { + // When user set axisIndex as a empty array, we think that user specify axisIndex + // but do not want use auto mode. Because empty array may be encountered when + // some error occured. + if (thisOption[dimNames.axisIndex] != null) { + hasIndexSpecified = true; + } + }, this); + + var orient = thisOption.orient; + + if (orient == null && hasIndexSpecified) { + return 'orient'; + } + else if (!hasIndexSpecified) { + if (orient == null) { + thisOption.orient = 'horizontal'; + } + return 'axisIndex'; + } + }, + + /** + * @private + */ + _autoSetAxisIndex: function () { + var autoAxisIndex = true; + var orient = this.get('orient', true); + var thisOption = this.option; + + if (autoAxisIndex) { + // Find axis that parallel to dataZoom as default. + var dimNames = orient === 'vertical' + ? {dim: 'y', axisIndex: 'yAxisIndex', axis: 'yAxis'} + : {dim: 'x', axisIndex: 'xAxisIndex', axis: 'xAxis'}; + + if (this.dependentModels[dimNames.axis].length) { + thisOption[dimNames.axisIndex] = [0]; + autoAxisIndex = false; + } + } + + if (autoAxisIndex) { + // Find the first category axis as default. (consider polar) + eachAxisDim(function (dimNames) { + if (!autoAxisIndex) { + return; + } + var axisIndices = []; + var axisModels = this.dependentModels[dimNames.axis]; + if (axisModels.length && !axisIndices.length) { + for (var i = 0, len = axisModels.length; i < len; i++) { + if (axisModels[i].get('type') === 'category') { + axisIndices.push(i); + } + } + } + thisOption[dimNames.axisIndex] = axisIndices; + if (axisIndices.length) { + autoAxisIndex = false; + } + }, this); + } + + if (autoAxisIndex) { + // FIXME + // 这里是兼容ec2的写法(没指定xAxisIndex和yAxisIndex时把scatter和双数值轴折柱纳入dataZoom控制), + // 但是实际是否需要Grid.js#getScaleByOption来判断(考虑time,log等axis type)? + + // If both dataZoom.xAxisIndex and dataZoom.yAxisIndex is not specified, + // dataZoom component auto adopts series that reference to + // both xAxis and yAxis which type is 'value'. + this.ecModel.eachSeries(function (seriesModel) { + if (this._isSeriesHasAllAxesTypeOf(seriesModel, 'value')) { + eachAxisDim(function (dimNames) { + var axisIndices = thisOption[dimNames.axisIndex]; + + var axisIndex = seriesModel.get(dimNames.axisIndex); + var axisId = seriesModel.get(dimNames.axisId); + + var axisModel = seriesModel.ecModel.queryComponents({ + mainType: dimNames.axis, + index: axisIndex, + id: axisId + })[0]; + + if (true) { + if (!axisModel) { + throw new Error( + dimNames.axis + ' "' + zrUtil.retrieve( + axisIndex, + axisId, + 0 + ) + '" not found' + ); + } + } + axisIndex = axisModel.componentIndex; + + if (zrUtil.indexOf(axisIndices, axisIndex) < 0) { + axisIndices.push(axisIndex); + } + }); + } + }, this); + } + }, + + /** + * @private + */ + _autoSetOrient: function () { + var dim; + + // Find the first axis + this.eachTargetAxis(function (dimNames) { + !dim && (dim = dimNames.name); + }, this); + + this.option.orient = dim === 'y' ? 'vertical' : 'horizontal'; + }, + + /** + * @private + */ + _isSeriesHasAllAxesTypeOf: function (seriesModel, axisType) { + // FIXME + // 需要series的xAxisIndex和yAxisIndex都首先自动设置上。 + // 例如series.type === scatter时。 + + var is = true; + eachAxisDim(function (dimNames) { + var seriesAxisIndex = seriesModel.get(dimNames.axisIndex); + var axisModel = this.dependentModels[dimNames.axis][seriesAxisIndex]; + + if (!axisModel || axisModel.get('type') !== axisType) { + is = false; + } + }, this); + return is; + }, + + /** + * @private + */ + _setDefaultThrottle: function (rawOption) { + // When first time user set throttle, auto throttle ends. + if (rawOption.hasOwnProperty('throttle')) { + this._autoThrottle = false; + } + if (this._autoThrottle) { + var globalOption = this.ecModel.option; + this.option.throttle = + (globalOption.animation && globalOption.animationDurationUpdate > 0) + ? 100 : 20; + } + }, + + /** + * @public + */ + getFirstTargetAxisModel: function () { + var firstAxisModel; + eachAxisDim(function (dimNames) { + if (firstAxisModel == null) { + var indices = this.get(dimNames.axisIndex); + if (indices.length) { + firstAxisModel = this.dependentModels[dimNames.axis][indices[0]]; + } + } + }, this); + + return firstAxisModel; + }, + + /** + * @public + * @param {Function} callback param: axisModel, dimNames, axisIndex, dataZoomModel, ecModel + */ + eachTargetAxis: function (callback, context) { + var ecModel = this.ecModel; + eachAxisDim(function (dimNames) { + each( + this.get(dimNames.axisIndex), + function (axisIndex) { + callback.call(context, dimNames, axisIndex, this, ecModel); + }, + this + ); + }, this); + }, + + getAxisProxy: function (dimName, axisIndex) { + return this._axisProxies[dimName + '_' + axisIndex]; + }, + + /** + * If not specified, set to undefined. + * + * @public + * @param {Object} opt + * @param {number} [opt.start] + * @param {number} [opt.end] + * @param {number} [opt.startValue] + * @param {number} [opt.endValue] + */ + setRawRange: function (opt) { + each(['start', 'end', 'startValue', 'endValue'], function (name) { + // If any of those prop is null/undefined, we should alos set + // them, because only one pair between start/end and + // startValue/endValue can work. + this.option[name] = opt[name]; + }, this); + }, + + /** + * @public + * @return {Array.} [startPercent, endPercent] + */ + getPercentRange: function () { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataPercentWindow(); + } + }, + + /** + * @public + * For example, chart.getModel().getComponent('dataZoom').getValueRange('y', 0); + * + * @param {string} [axisDimName] + * @param {number} [axisIndex] + * @return {Array.} [startValue, endValue] + */ + getValueRange: function (axisDimName, axisIndex) { + if (axisDimName == null && axisIndex == null) { + var axisProxy = this.findRepresentativeAxisProxy(); + if (axisProxy) { + return axisProxy.getDataValueWindow(); + } + } + else { + return this.getAxisProxy(axisDimName, axisIndex).getDataValueWindow(); + } + }, + + /** + * @public + * @return {module:echarts/component/dataZoom/AxisProxy} + */ + findRepresentativeAxisProxy: function () { + // Find the first hosted axisProxy + var axisProxies = this._axisProxies; + for (var key in axisProxies) { + if (axisProxies.hasOwnProperty(key) && axisProxies[key].hostedBy(this)) { + return axisProxies[key]; + } + } + + // If no hosted axis find not hosted axisProxy. + // Consider this case: dataZoomModel1 and dataZoomModel2 control the same axis, + // and the option.start or option.end settings are different. The percentRange + // should follow axisProxy. + // (We encounter this problem in toolbox data zoom.) + for (var key in axisProxies) { + if (axisProxies.hasOwnProperty(key) && !axisProxies[key].hostedBy(this)) { + return axisProxies[key]; + } + } + } + + }); + + function retrieveRaw(option) { + var ret = {}; + each( + ['start', 'end', 'startValue', 'endValue', 'throttle'], + function (name) { + option.hasOwnProperty(name) && (ret[name] = option[name]); + } + ); + return ret; + } + + function processRangeProp(percentProp, valueProp, rawOption, thisOption) { + // start/end has higher priority over startValue/endValue, + // but we should make chart.setOption({endValue: 1000}) effective, + // rather than chart.setOption({endValue: 1000, end: null}). + if (rawOption[valueProp] != null && rawOption[percentProp] == null) { + thisOption[percentProp] = null; + } + // Otherwise do nothing and use the merge result. + } + + module.exports = DataZoomModel; + + + +/***/ }, +/* 319 */ +/***/ function(module, exports, __webpack_require__) { + + + var formatUtil = __webpack_require__(6); + var zrUtil = __webpack_require__(4); + + var helper = {}; + + var AXIS_DIMS = ['x', 'y', 'z', 'radius', 'angle']; + + /** + * Create "each" method to iterate names. + * + * @pubilc + * @param {Array.} names + * @param {Array.=} attrs + * @return {Function} + */ + helper.createNameEach = function (names, attrs) { + names = names.slice(); + var capitalNames = zrUtil.map(names, formatUtil.capitalFirst); + attrs = (attrs || []).slice(); + var capitalAttrs = zrUtil.map(attrs, formatUtil.capitalFirst); + + return function (callback, context) { + zrUtil.each(names, function (name, index) { + var nameObj = {name: name, capital: capitalNames[index]}; + + for (var j = 0; j < attrs.length; j++) { + nameObj[attrs[j]] = name + capitalAttrs[j]; + } + + callback.call(context, nameObj); + }); + }; + }; + + /** + * Iterate each dimension name. + * + * @public + * @param {Function} callback The parameter is like: + * { + * name: 'angle', + * capital: 'Angle', + * axis: 'angleAxis', + * axisIndex: 'angleAixs', + * index: 'angleIndex' + * } + * @param {Object} context + */ + helper.eachAxisDim = helper.createNameEach(AXIS_DIMS, ['axisIndex', 'axis', 'index', 'id']); + + /** + * If tow dataZoomModels has the same axis controlled, we say that they are 'linked'. + * dataZoomModels and 'links' make up one or more graphics. + * This function finds the graphic where the source dataZoomModel is in. + * + * @public + * @param {Function} forEachNode Node iterator. + * @param {Function} forEachEdgeType edgeType iterator + * @param {Function} edgeIdGetter Giving node and edgeType, return an array of edge id. + * @return {Function} Input: sourceNode, Output: Like {nodes: [], dims: {}} + */ + helper.createLinkedNodesFinder = function (forEachNode, forEachEdgeType, edgeIdGetter) { + + return function (sourceNode) { + var result = { + nodes: [], + records: {} // key: edgeType.name, value: Object (key: edge id, value: boolean). + }; + + forEachEdgeType(function (edgeType) { + result.records[edgeType.name] = {}; + }); + + if (!sourceNode) { + return result; + } + + absorb(sourceNode, result); + + var existsLink; + do { + existsLink = false; + forEachNode(processSingleNode); + } + while (existsLink); + + function processSingleNode(node) { + if (!isNodeAbsorded(node, result) && isLinked(node, result)) { + absorb(node, result); + existsLink = true; + } + } + + return result; + }; + + function isNodeAbsorded(node, result) { + return zrUtil.indexOf(result.nodes, node) >= 0; + } + + function isLinked(node, result) { + var hasLink = false; + forEachEdgeType(function (edgeType) { + zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) { + result.records[edgeType.name][edgeId] && (hasLink = true); + }); + }); + return hasLink; + } + + function absorb(node, result) { + result.nodes.push(node); + forEachEdgeType(function (edgeType) { + zrUtil.each(edgeIdGetter(node, edgeType) || [], function (edgeId) { + result.records[edgeType.name][edgeId] = true; + }); + }); + } + }; + + module.exports = helper; + + +/***/ }, +/* 320 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Axis operator + */ + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var each = zrUtil.each; + var asc = numberUtil.asc; + + /** + * Operate single axis. + * One axis can only operated by one axis operator. + * Different dataZoomModels may be defined to operate the same axis. + * (i.e. 'inside' data zoom and 'slider' data zoom components) + * So dataZoomModels share one axisProxy in that case. + * + * @class + */ + var AxisProxy = function (dimName, axisIndex, dataZoomModel, ecModel) { + + /** + * @private + * @type {string} + */ + this._dimName = dimName; + + /** + * @private + */ + this._axisIndex = axisIndex; + + /** + * @private + * @type {Array.} + */ + this._valueWindow; + + /** + * @private + * @type {Array.} + */ + this._percentWindow; + + /** + * @private + * @type {Array.} + */ + this._dataExtent; + + /** + * @readOnly + * @type {module: echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @private + * @type {module: echarts/component/dataZoom/DataZoomModel} + */ + this._dataZoomModel = dataZoomModel; + }; + + AxisProxy.prototype = { + + constructor: AxisProxy, + + /** + * Whether the axisProxy is hosted by dataZoomModel. + * + * @public + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + * @return {boolean} + */ + hostedBy: function (dataZoomModel) { + return this._dataZoomModel === dataZoomModel; + }, + + /** + * @return {Array.} + */ + getDataExtent: function () { + return this._dataExtent.slice(); + }, + + /** + * @return {Array.} + */ + getDataValueWindow: function () { + return this._valueWindow.slice(); + }, + + /** + * @return {Array.} + */ + getDataPercentWindow: function () { + return this._percentWindow.slice(); + }, + + /** + * @public + * @param {number} axisIndex + * @return {Array} seriesModels + */ + getTargetSeriesModels: function () { + var seriesModels = []; + var ecModel = this.ecModel; + + ecModel.eachSeries(function (seriesModel) { + var dimName = this._dimName; + var axisModel = ecModel.queryComponents({ + mainType: dimName + 'Axis', + index: seriesModel.get(dimName + 'AxisIndex'), + id: seriesModel.get(dimName + 'AxisId') + })[0]; + if (this._axisIndex === (axisModel && axisModel.componentIndex)) { + seriesModels.push(seriesModel); + } + }, this); + + return seriesModels; + }, + + getAxisModel: function () { + return this.ecModel.getComponent(this._dimName + 'Axis', this._axisIndex); + }, + + getOtherAxisModel: function () { + var axisDim = this._dimName; + var ecModel = this.ecModel; + var axisModel = this.getAxisModel(); + var isCartesian = axisDim === 'x' || axisDim === 'y'; + var otherAxisDim; + var coordSysIndexName; + if (isCartesian) { + coordSysIndexName = 'gridIndex'; + otherAxisDim = axisDim === 'x' ? 'y' : 'x'; + } + else { + coordSysIndexName = 'polarIndex'; + otherAxisDim = axisDim === 'angle' ? 'radius' : 'angle'; + } + var foundOtherAxisModel; + ecModel.eachComponent(otherAxisDim + 'Axis', function (otherAxisModel) { + if ((otherAxisModel.get(coordSysIndexName) || 0) + === (axisModel.get(coordSysIndexName) || 0) + ) { + foundOtherAxisModel = otherAxisModel; + } + }); + return foundOtherAxisModel; + }, + + /** + * Notice: reset should not be called before series.restoreData() called, + * so it is recommanded to be called in "process stage" but not "model init + * stage". + * + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + reset: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + // Culculate data window and data extent, and record them. + var dataExtent = this._dataExtent = calculateDataExtent( + this._dimName, this.getTargetSeriesModels() + ); + var dataWindow = calculateDataWindow( + dataZoomModel.option, dataExtent, this + ); + this._valueWindow = dataWindow.valueWindow; + this._percentWindow = dataWindow.percentWindow; + + // Update axis setting then. + setAxisModel(this); + }, + + /** + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + restore: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + this._valueWindow = this._percentWindow = null; + setAxisModel(this, true); + }, + + /** + * @param {module: echarts/component/dataZoom/DataZoomModel} dataZoomModel + */ + filterData: function (dataZoomModel) { + if (dataZoomModel !== this._dataZoomModel) { + return; + } + + var axisDim = this._dimName; + var seriesModels = this.getTargetSeriesModels(); + var filterMode = dataZoomModel.get('filterMode'); + var valueWindow = this._valueWindow; + + // FIXME + // Toolbox may has dataZoom injected. And if there are stacked bar chart + // with NaN data, NaN will be filtered and stack will be wrong. + // So we need to force the mode to be set empty. + // In fect, it is not a big deal that do not support filterMode-'filter' + // when using toolbox#dataZoom, utill tooltip#dataZoom support "single axis + // selection" some day, which might need "adapt to data extent on the + // otherAxis", which is disabled by filterMode-'empty'. + var otherAxisModel = this.getOtherAxisModel(); + if (dataZoomModel.get('$fromToolbox') + && otherAxisModel + && otherAxisModel.get('type') === 'category' + ) { + filterMode = 'empty'; + } + + // Process series data + each(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + + seriesData && each(seriesModel.coordDimToDataDim(axisDim), function (dim) { + if (filterMode === 'empty') { + seriesModel.setData( + seriesData.map(dim, function (value) { + return !isInWindow(value) ? NaN : value; + }) + ); + } + else { + seriesData.filterSelf(dim, isInWindow); + } + }); + }); + + function isInWindow(value) { + return value >= valueWindow[0] && value <= valueWindow[1]; + } + } + }; + + function calculateDataExtent(axisDim, seriesModels) { + var dataExtent = [Infinity, -Infinity]; + + each(seriesModels, function (seriesModel) { + var seriesData = seriesModel.getData(); + if (seriesData) { + each(seriesModel.coordDimToDataDim(axisDim), function (dim) { + var seriesExtent = seriesData.getDataExtent(dim); + seriesExtent[0] < dataExtent[0] && (dataExtent[0] = seriesExtent[0]); + seriesExtent[1] > dataExtent[1] && (dataExtent[1] = seriesExtent[1]); + }); + } + }, this); + + return dataExtent; + } + + function calculateDataWindow(opt, dataExtent, axisProxy) { + var axisModel = axisProxy.getAxisModel(); + var scale = axisModel.axis.scale; + var percentExtent = [0, 100]; + var percentWindow = [ + opt.start, + opt.end + ]; + var valueWindow = []; + + // In percent range is used and axis min/max/scale is set, + // window should be based on min/max/0, but should not be + // based on the extent of filtered data. + dataExtent = dataExtent.slice(); + fixExtendByAxis(dataExtent, axisModel, scale); + + each(['startValue', 'endValue'], function (prop) { + valueWindow.push( + opt[prop] != null + ? scale.parse(opt[prop]) + : null + ); + }); + + // Normalize bound. + each([0, 1], function (idx) { + var boundValue = valueWindow[idx]; + var boundPercent = percentWindow[idx]; + + // start/end has higher priority over startValue/endValue, + // because start/end can be consistent among different type + // of axis but startValue/endValue not. + + if (boundPercent != null || boundValue == null) { + if (boundPercent == null) { + boundPercent = percentExtent[idx]; + } + // Use scale.parse to math round for category or time axis. + boundValue = scale.parse(numberUtil.linearMap( + boundPercent, percentExtent, dataExtent, true + )); + } + else { // boundPercent == null && boundValue != null + boundPercent = numberUtil.linearMap( + boundValue, dataExtent, percentExtent, true + ); + } + // valueWindow[idx] = round(boundValue); + // percentWindow[idx] = round(boundPercent); + valueWindow[idx] = boundValue; + percentWindow[idx] = boundPercent; + }); + + return { + valueWindow: asc(valueWindow), + percentWindow: asc(percentWindow) + }; + } + + function fixExtendByAxis(dataExtent, axisModel, scale) { + each(['min', 'max'], function (minMax, index) { + var axisMax = axisModel.get(minMax, true); + // Consider 'dataMin', 'dataMax' + if (axisMax != null && (axisMax + '').toLowerCase() !== 'data' + minMax) { + dataExtent[index] = scale.parse(axisMax); + } + }); + + if (!axisModel.get('scale', true)) { + dataExtent[0] > 0 && (dataExtent[0] = 0); + dataExtent[1] < 0 && (dataExtent[1] = 0); + } + + return dataExtent; + } + + function setAxisModel(axisProxy, isRestore) { + var axisModel = axisProxy.getAxisModel(); + + var percentWindow = axisProxy._percentWindow; + var valueWindow = axisProxy._valueWindow; + + if (!percentWindow) { + return; + } + + var isFull = isRestore || (percentWindow[0] === 0 && percentWindow[1] === 100); + // [0, 500]: arbitrary value, guess axis extent. + var precision = !isRestore && numberUtil.getPixelPrecision(valueWindow, [0, 500]); + // toFixed() digits argument must be between 0 and 20 + var invalidPrecision = !isRestore && !(precision < 20 && precision >= 0); + + var useOrigin = isRestore || isFull || invalidPrecision; + + axisModel.setRange && axisModel.setRange( + useOrigin ? null : +valueWindow[0].toFixed(precision), + useOrigin ? null : +valueWindow[1].toFixed(precision) + ); + } + + module.exports = AxisProxy; + + + +/***/ }, +/* 321 */ +/***/ function(module, exports, __webpack_require__) { + + + + var ComponentView = __webpack_require__(29); + + module.exports = ComponentView.extend({ + + type: 'dataZoom', + + render: function (dataZoomModel, ecModel, api, payload) { + this.dataZoomModel = dataZoomModel; + this.ecModel = ecModel; + this.api = api; + }, + + /** + * Find the first target coordinate system. + * + * @protected + * @return {Object} { + * cartesians: [ + * {model: coord0, axisModels: [axis1, axis3], coordIndex: 1}, + * {model: coord1, axisModels: [axis0, axis2], coordIndex: 0}, + * ... + * ], // cartesians must not be null/undefined. + * polars: [ + * {model: coord0, axisModels: [axis4], coordIndex: 0}, + * ... + * ], // polars must not be null/undefined. + * axisModels: [axis0, axis1, axis2, axis3, axis4] + * // axisModels must not be null/undefined. + * } + */ + getTargetInfo: function () { + var dataZoomModel = this.dataZoomModel; + var ecModel = this.ecModel; + var cartesians = []; + var polars = []; + var axisModels = []; + + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + var axisModel = ecModel.getComponent(dimNames.axis, axisIndex); + if (axisModel) { + axisModels.push(axisModel); + var coordSysName; + if (dimNames.axis === 'xAxis' || dimNames.axis === 'yAxis') { + coordSysName = 'grid'; + } + else { + // Polar + coordSysName = 'polar'; + } + var coordModel = ecModel.queryComponents({ + mainType: coordSysName, + index: axisModel.get(coordSysName + 'Index'), + id: axisModel.get(coordSysName + 'Id') + })[0]; + + if (coordModel != null) { + save(coordModel, axisModel, coordSysName === 'grid' ? cartesians : polars, coordModel.componentIndex); + } + } + }, this); + + function save(coordModel, axisModel, store, coordIndex) { + var item; + for (var i = 0; i < store.length; i++) { + if (store[i].model === coordModel) { + item = store[i]; + break; + } + } + if (!item) { + store.push(item = { + model: coordModel, axisModels: [], coordIndex: coordIndex + }); + } + item.axisModels.push(axisModel); + } + + return { + cartesians: cartesians, + polars: polars, + axisModels: axisModels + }; + } + + }); + + + +/***/ }, +/* 322 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var DataZoomModel = __webpack_require__(318); + + var SliderZoomModel = DataZoomModel.extend({ + + type: 'dataZoom.slider', + + layoutMode: 'box', + + /** + * @protected + */ + defaultOption: { + show: true, + + // ph => placeholder. Using placehoder here because + // deault value can only be drived in view stage. + right: 'ph', // Default align to grid rect. + top: 'ph', // Default align to grid rect. + width: 'ph', // Default align to grid rect. + height: 'ph', // Default align to grid rect. + left: null, // Default align to grid rect. + bottom: null, // Default align to grid rect. + + backgroundColor: 'rgba(47,69,84,0)', // Background of slider zoom component. + // dataBackgroundColor: '#ddd', // Background coor of data shadow and border of box, + // highest priority, remain for compatibility of + // previous version, but not recommended any more. + dataBackground: { + lineStyle: { + color: '#2f4554', + width: 0.5, + opacity: 0.3 + }, + areaStyle: { + color: 'rgba(47,69,84,0.3)', + opacity: 0.3 + } + }, + borderColor: '#ddd', // border color of the box. For compatibility, + // if dataBackgroundColor is set, borderColor + // is ignored. + + fillerColor: 'rgba(167,183,204,0.4)', // Color of selected area. + // handleColor: 'rgba(89,170,216,0.95)', // Color of handle. + // handleIcon: 'path://M4.9,17.8c0-1.4,4.5-10.5,5.5-12.4c0-0.1,0.6-1.1,0.9-1.1c0.4,0,0.9,1,0.9,1.1c1.1,2.2,5.4,11,5.4,12.4v17.8c0,1.5-0.6,2.1-1.3,2.1H6.1c-0.7,0-1.3-0.6-1.3-2.1V17.8z', + handleIcon: 'M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z', + // Percent of the slider height + handleSize: '100%', + + handleStyle: { + color: '#a7b7cc' + }, + + labelPrecision: null, + labelFormatter: null, + showDetail: true, + showDataShadow: 'auto', // Default auto decision. + realtime: true, + zoomLock: false, // Whether disable zoom. + textStyle: { + color: '#333' + } + } + + }); + + module.exports = SliderZoomModel; + + + +/***/ }, +/* 323 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var throttle = __webpack_require__(308); + var DataZoomView = __webpack_require__(321); + var Rect = graphic.Rect; + var numberUtil = __webpack_require__(7); + var linearMap = numberUtil.linearMap; + var layout = __webpack_require__(21); + var sliderMove = __webpack_require__(324); + var asc = numberUtil.asc; + var bind = zrUtil.bind; + // var mathMax = Math.max; + var each = zrUtil.each; + + // Constants + var DEFAULT_LOCATION_EDGE_GAP = 7; + var DEFAULT_FRAME_BORDER_WIDTH = 1; + var DEFAULT_FILLER_SIZE = 30; + var HORIZONTAL = 'horizontal'; + var VERTICAL = 'vertical'; + var LABEL_GAP = 5; + var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; + + var SliderZoomView = DataZoomView.extend({ + + type: 'dataZoom.slider', + + init: function (ecModel, api) { + + /** + * @private + * @type {Object} + */ + this._displayables = {}; + + /** + * @private + * @type {string} + */ + this._orient; + + /** + * [0, 100] + * @private + */ + this._range; + + /** + * [coord of the first handle, coord of the second handle] + * @private + */ + this._handleEnds; + + /** + * [length, thick] + * @private + * @type {Array.} + */ + this._size; + + /** + * @private + * @type {number} + */ + this._handleWidth; + + /** + * @private + * @type {number} + */ + this._handleHeight; + + /** + * @private + */ + this._location; + + /** + * @private + */ + this._dragging; + + /** + * @private + */ + this._dataShadowInfo; + + this.api = api; + }, + + /** + * @override + */ + render: function (dataZoomModel, ecModel, api, payload) { + SliderZoomView.superApply(this, 'render', arguments); + + throttle.createOrUpdate( + this, + '_dispatchZoomAction', + this.dataZoomModel.get('throttle'), + 'fixRate' + ); + + this._orient = dataZoomModel.get('orient'); + + if (this.dataZoomModel.get('show') === false) { + this.group.removeAll(); + return; + } + + // Notice: this._resetInterval() should not be executed when payload.type + // is 'dataZoom', origin this._range should be maintained, otherwise 'pan' + // or 'zoom' info will be missed because of 'throttle' of this.dispatchAction, + if (!payload || payload.type !== 'dataZoom' || payload.from !== this.uid) { + this._buildView(); + } + + this._updateView(); + }, + + /** + * @override + */ + remove: function () { + SliderZoomView.superApply(this, 'remove', arguments); + throttle.clear(this, '_dispatchZoomAction'); + }, + + /** + * @override + */ + dispose: function () { + SliderZoomView.superApply(this, 'dispose', arguments); + throttle.clear(this, '_dispatchZoomAction'); + }, + + _buildView: function () { + var thisGroup = this.group; + + thisGroup.removeAll(); + + this._resetLocation(); + this._resetInterval(); + + var barGroup = this._displayables.barGroup = new graphic.Group(); + + this._renderBackground(); + + this._renderHandle(); + + this._renderDataShadow(); + + thisGroup.add(barGroup); + + this._positionGroup(); + }, + + /** + * @private + */ + _resetLocation: function () { + var dataZoomModel = this.dataZoomModel; + var api = this.api; + + // If some of x/y/width/height are not specified, + // auto-adapt according to target grid. + var coordRect = this._findCoordRect(); + var ecSize = {width: api.getWidth(), height: api.getHeight()}; + // Default align by coordinate system rect. + var positionInfo = this._orient === HORIZONTAL + ? { + // Why using 'right', because right should be used in vertical, + // and it is better to be consistent for dealing with position param merge. + right: ecSize.width - coordRect.x - coordRect.width, + top: (ecSize.height - DEFAULT_FILLER_SIZE - DEFAULT_LOCATION_EDGE_GAP), + width: coordRect.width, + height: DEFAULT_FILLER_SIZE + } + : { // vertical + right: DEFAULT_LOCATION_EDGE_GAP, + top: coordRect.y, + width: DEFAULT_FILLER_SIZE, + height: coordRect.height + }; + + // Do not write back to option and replace value 'ph', because + // the 'ph' value should be recalculated when resize. + var layoutParams = layout.getLayoutParams(dataZoomModel.option); + + // Replace the placeholder value. + zrUtil.each(['right', 'top', 'width', 'height'], function (name) { + if (layoutParams[name] === 'ph') { + layoutParams[name] = positionInfo[name]; + } + }); + + var layoutRect = layout.getLayoutRect( + layoutParams, + ecSize, + dataZoomModel.padding + ); + + this._location = {x: layoutRect.x, y: layoutRect.y}; + this._size = [layoutRect.width, layoutRect.height]; + this._orient === VERTICAL && this._size.reverse(); + }, + + /** + * @private + */ + _positionGroup: function () { + var thisGroup = this.group; + var location = this._location; + var orient = this._orient; + + // Just use the first axis to determine mapping. + var targetAxisModel = this.dataZoomModel.getFirstTargetAxisModel(); + var inverse = targetAxisModel && targetAxisModel.get('inverse'); + + var barGroup = this._displayables.barGroup; + var otherAxisInverse = (this._dataShadowInfo || {}).otherAxisInverse; + + // Transform barGroup. + barGroup.attr( + (orient === HORIZONTAL && !inverse) + ? {scale: otherAxisInverse ? [1, 1] : [1, -1]} + : (orient === HORIZONTAL && inverse) + ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]} + : (orient === VERTICAL && !inverse) + ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2} + // Dont use Math.PI, considering shadow direction. + : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2} + ); + + // Position barGroup + var rect = thisGroup.getBoundingRect([barGroup]); + thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]); + }, + + /** + * @private + */ + _getViewExtent: function () { + return [0, this._size[0]]; + }, + + _renderBackground : function () { + var dataZoomModel = this.dataZoomModel; + var size = this._size; + + this._displayables.barGroup.add(new Rect({ + silent: true, + shape: { + x: 0, y: 0, width: size[0], height: size[1] + }, + style: { + fill: dataZoomModel.get('backgroundColor') + }, + z2: -40 + })); + }, + + _renderDataShadow: function () { + var info = this._dataShadowInfo = this._prepareDataShadowInfo(); + + if (!info) { + return; + } + + var size = this._size; + var seriesModel = info.series; + var data = seriesModel.getRawData(); + var otherDim = seriesModel.getShadowDim + ? seriesModel.getShadowDim() // @see candlestick + : info.otherDim; + + var otherDataExtent = data.getDataExtent(otherDim); + // Nice extent. + var otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3; + otherDataExtent = [ + otherDataExtent[0] - otherOffset, + otherDataExtent[1] + otherOffset + ]; + var otherShadowExtent = [0, size[1]]; + + var thisShadowExtent = [0, size[0]]; + + var areaPoints = [[size[0], 0], [0, 0]]; + var linePoints = []; + var step = thisShadowExtent[1] / (data.count() - 1); + var thisCoord = 0; + + // Optimize for large data shadow + var stride = Math.round(data.count() / size[0]); + data.each([otherDim], function (value, index) { + if (stride > 0 && (index % stride)) { + thisCoord += step; + return; + } + // FIXME + // 应该使用统计的空判断?还是在list里进行空判断? + var otherCoord = (value == null || isNaN(value) || value === '') + ? null + : linearMap(value, otherDataExtent, otherShadowExtent, true); + if (otherCoord != null) { + areaPoints.push([thisCoord, otherCoord]); + linePoints.push([thisCoord, otherCoord]); + } + + thisCoord += step; + }); + + var dataZoomModel = this.dataZoomModel; + // var dataBackgroundModel = dataZoomModel.getModel('dataBackground'); + this._displayables.barGroup.add(new graphic.Polygon({ + shape: {points: areaPoints}, + style: zrUtil.defaults( + {fill: dataZoomModel.get('dataBackgroundColor')}, + dataZoomModel.getModel('dataBackground.areaStyle').getAreaStyle() + ), + silent: true, + z2: -20 + })); + this._displayables.barGroup.add(new graphic.Polyline({ + shape: {points: linePoints}, + style: dataZoomModel.getModel('dataBackground.lineStyle').getLineStyle(), + silent: true, + z2: -19 + })); + }, + + _prepareDataShadowInfo: function () { + var dataZoomModel = this.dataZoomModel; + var showDataShadow = dataZoomModel.get('showDataShadow'); + + if (showDataShadow === false) { + return; + } + + // Find a representative series. + var result; + var ecModel = this.ecModel; + + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + var seriesModels = dataZoomModel + .getAxisProxy(dimNames.name, axisIndex) + .getTargetSeriesModels(); + + zrUtil.each(seriesModels, function (seriesModel) { + if (result) { + return; + } + + if (showDataShadow !== true && zrUtil.indexOf( + SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type') + ) < 0 + ) { + return; + } + + var otherDim = getOtherDim(dimNames.name); + + var thisAxis = ecModel.getComponent(dimNames.axis, axisIndex).axis; + + result = { + thisAxis: thisAxis, + series: seriesModel, + thisDim: dimNames.name, + otherDim: otherDim, + otherAxisInverse: seriesModel + .coordinateSystem.getOtherAxis(thisAxis).inverse + }; + + }, this); + + }, this); + + return result; + }, + + _renderHandle: function () { + var displaybles = this._displayables; + var handles = displaybles.handles = []; + var handleLabels = displaybles.handleLabels = []; + var barGroup = this._displayables.barGroup; + var size = this._size; + var dataZoomModel = this.dataZoomModel; + + barGroup.add(displaybles.filler = new Rect({ + draggable: true, + cursor: 'move', + drift: bind(this._onDragMove, this, 'all'), + ondragstart: bind(this._showDataInfo, this, true), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false), + style: { + fill: dataZoomModel.get('fillerColor'), + textPosition : 'inside' + } + })); + + // Frame border. + barGroup.add(new Rect(graphic.subPixelOptimizeRect({ + silent: true, + shape: { + x: 0, + y: 0, + width: size[0], + height: size[1] + }, + style: { + stroke: dataZoomModel.get('dataBackgroundColor') + || dataZoomModel.get('borderColor'), + lineWidth: DEFAULT_FRAME_BORDER_WIDTH, + fill: 'rgba(0,0,0,0)' + } + }))); + + var iconStr = dataZoomModel.get('handleIcon'); + each([0, 1], function (handleIndex) { + var path = graphic.makePath(iconStr, { + style: { + strokeNoScale: true + }, + rectHover: true, + cursor: this._orient === 'vertical' ? 'ns-resize' : 'ew-resize', + draggable: true, + drift: bind(this._onDragMove, this, handleIndex), + ondragend: bind(this._onDragEnd, this), + onmouseover: bind(this._showDataInfo, this, true), + onmouseout: bind(this._showDataInfo, this, false) + }, { + x: -0.5, + y: 0, + width: 1, + height: 1 + }, 'center'); + + var bRect = path.getBoundingRect(); + this._handleHeight = numberUtil.parsePercent(dataZoomModel.get('handleSize'), this._size[1]); + this._handleWidth = bRect.width / bRect.height * this._handleHeight; + + path.setStyle(dataZoomModel.getModel('handleStyle').getItemStyle()); + var handleColor = dataZoomModel.get('handleColor'); + // Compatitable with previous version + if (handleColor != null) { + path.style.fill = handleColor; + } + + barGroup.add(handles[handleIndex] = path); + + var textStyleModel = dataZoomModel.textStyleModel; + + this.group.add( + handleLabels[handleIndex] = new graphic.Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textVerticalAlign: 'middle', + textAlign: 'center', + fill: textStyleModel.getTextColor(), + textFont: textStyleModel.getFont() + }, + z2: 10 + })); + + }, this); + }, + + /** + * @private + */ + _resetInterval: function () { + var range = this._range = this.dataZoomModel.getPercentRange(); + var viewExtent = this._getViewExtent(); + + this._handleEnds = [ + linearMap(range[0], [0, 100], viewExtent, true), + linearMap(range[1], [0, 100], viewExtent, true) + ]; + }, + + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} dx + * @param {number} dy + */ + _updateInterval: function (handleIndex, delta) { + var handleEnds = this._handleEnds; + var viewExtend = this._getViewExtent(); + + sliderMove( + delta, + handleEnds, + viewExtend, + (handleIndex === 'all' || this.dataZoomModel.get('zoomLock')) + ? 'rigid' : 'cross', + handleIndex + ); + + this._range = asc([ + linearMap(handleEnds[0], viewExtend, [0, 100], true), + linearMap(handleEnds[1], viewExtend, [0, 100], true) + ]); + }, + + /** + * @private + */ + _updateView: function () { + var displaybles = this._displayables; + var handleEnds = this._handleEnds; + var handleInterval = asc(handleEnds.slice()); + var size = this._size; + + each([0, 1], function (handleIndex) { + // Handles + var handle = displaybles.handles[handleIndex]; + var handleHeight = this._handleHeight; + handle.attr({ + scale: [handleHeight, handleHeight], + position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2] + }); + }, this); + + // Filler + displaybles.filler.setShape({ + x: handleInterval[0], + y: 0, + width: handleInterval[1] - handleInterval[0], + height: size[1] + }); + + this._updateDataInfo(); + }, + + /** + * @private + */ + _updateDataInfo: function () { + var dataZoomModel = this.dataZoomModel; + var displaybles = this._displayables; + var handleLabels = displaybles.handleLabels; + var orient = this._orient; + var labelTexts = ['', '']; + + // FIXME + // date型,支持formatter,autoformatter(ec2 date.getAutoFormatter) + if (dataZoomModel.get('showDetail')) { + var dataInterval; + var axis; + dataZoomModel.eachTargetAxis(function (dimNames, axisIndex) { + // Using dataInterval of the first axis. + if (!dataInterval) { + dataInterval = dataZoomModel + .getAxisProxy(dimNames.name, axisIndex) + .getDataValueWindow(); + axis = this.ecModel.getComponent(dimNames.axis, axisIndex).axis; + } + }, this); + + if (dataInterval) { + labelTexts = [ + this._formatLabel(dataInterval[0], axis), + this._formatLabel(dataInterval[1], axis) + ]; + } + } + + var orderedHandleEnds = asc(this._handleEnds.slice()); + + setLabel.call(this, 0); + setLabel.call(this, 1); + + function setLabel(handleIndex) { + // Label + // Text should not transform by barGroup. + // Ignore handlers transform + var barTransform = graphic.getTransform( + displaybles.handles[handleIndex].parent, this.group + ); + var direction = graphic.transformDirection( + handleIndex === 0 ? 'right' : 'left', barTransform + ); + var offset = this._handleWidth / 2 + LABEL_GAP; + var textPoint = graphic.applyTransform( + [ + orderedHandleEnds[handleIndex] + (handleIndex === 0 ? -offset : offset), + this._size[1] / 2 + ], + barTransform + ); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + textVerticalAlign: orient === HORIZONTAL ? 'middle' : direction, + textAlign: orient === HORIZONTAL ? direction : 'center', + text: labelTexts[handleIndex] + }); + } + }, + + /** + * @private + */ + _formatLabel: function (value, axis) { + var dataZoomModel = this.dataZoomModel; + var labelFormatter = dataZoomModel.get('labelFormatter'); + if (zrUtil.isFunction(labelFormatter)) { + return labelFormatter(value); + } + + var labelPrecision = dataZoomModel.get('labelPrecision'); + if (labelPrecision == null || labelPrecision === 'auto') { + labelPrecision = axis.getPixelPrecision(); + } + + value = (value == null && isNaN(value)) + ? '' + // FIXME Glue code + : (axis.type === 'category' || axis.type === 'time') + ? axis.scale.getLabel(Math.round(value)) + // param of toFixed should less then 20. + : value.toFixed(Math.min(labelPrecision, 20)); + + if (zrUtil.isString(labelFormatter)) { + value = labelFormatter.replace('{value}', value); + } + + return value; + }, + + /** + * @private + * @param {boolean} showOrHide true: show, false: hide + */ + _showDataInfo: function (showOrHide) { + // Always show when drgging. + showOrHide = this._dragging || showOrHide; + + var handleLabels = this._displayables.handleLabels; + handleLabels[0].attr('invisible', !showOrHide); + handleLabels[1].attr('invisible', !showOrHide); + }, + + _onDragMove: function (handleIndex, dx, dy) { + this._dragging = true; + + // Transform dx, dy to bar coordination. + var vertex = this._applyBarTransform([dx, dy], true); + + this._updateInterval(handleIndex, vertex[0]); + this._updateView(); + + if (this.dataZoomModel.get('realtime')) { + this._dispatchZoomAction(); + } + }, + + _onDragEnd: function () { + this._dragging = false; + this._showDataInfo(false); + this._dispatchZoomAction(); + }, + + /** + * This action will be throttled. + * @private + */ + _dispatchZoomAction: function () { + var range = this._range; + + this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + dataZoomId: this.dataZoomModel.id, + start: range[0], + end: range[1] + }); + }, + + /** + * @private + */ + _applyBarTransform: function (vertex, inverse) { + var barTransform = this._displayables.barGroup.getLocalTransform(); + return graphic.applyTransform(vertex, barTransform, inverse); + }, + + /** + * @private + */ + _findCoordRect: function () { + // Find the grid coresponding to the first axis referred by dataZoom. + var targetInfo = this.getTargetInfo(); + + // FIXME + // 判断是catesian还是polar + var rect; + if (targetInfo.cartesians.length) { + rect = targetInfo.cartesians[0].model.coordinateSystem.getRect(); + } + else { // Polar + // FIXME + // 暂时随便写的 + var width = this.api.getWidth(); + var height = this.api.getHeight(); + rect = { + x: width * 0.2, + y: height * 0.2, + width: width * 0.6, + height: height * 0.6 + }; + } + + return rect; + } + + }); + + function getOtherDim(thisDim) { + // FIXME + // 这个逻辑和getOtherAxis里一致,但是写在这里是否不好 + return thisDim === 'x' ? 'y' : 'x'; + } + + module.exports = SliderZoomView; + + + +/***/ }, +/* 324 */ +/***/ function(module, exports) { + + + + /** + * Calculate slider move result. + * + * @param {number} delta Move length. + * @param {Array.} handleEnds handleEnds[0] and be bigger then handleEnds[1]. + * handleEnds will be modified in this method. + * @param {Array.} extent handleEnds is restricted by extent. + * extent[0] should less or equals than extent[1]. + * @param {string} mode 'rigid': Math.abs(handleEnds[0] - handleEnds[1]) remain unchanged, + * 'cross' handleEnds[0] can be bigger then handleEnds[1], + * 'push' handleEnds[0] can not be bigger then handleEnds[1], + * when they touch, one push other. + * @param {number} handleIndex If mode is 'rigid', handleIndex is not required. + * @param {Array.} The input handleEnds. + */ + module.exports = function (delta, handleEnds, extent, mode, handleIndex) { + if (!delta) { + return handleEnds; + } + + if (mode === 'rigid') { + delta = getRealDelta(delta, handleEnds, extent); + handleEnds[0] += delta; + handleEnds[1] += delta; + } + else { + delta = getRealDelta(delta, handleEnds[handleIndex], extent); + handleEnds[handleIndex] += delta; + + if (mode === 'push' && handleEnds[0] > handleEnds[1]) { + handleEnds[1 - handleIndex] = handleEnds[handleIndex]; + } + } + + return handleEnds; + + function getRealDelta(delta, handleEnds, extent) { + var handleMinMax = !handleEnds.length + ? [handleEnds, handleEnds] + : handleEnds.slice(); + handleEnds[0] > handleEnds[1] && handleMinMax.reverse(); + + if (delta < 0 && handleMinMax[0] + delta < extent[0]) { + delta = extent[0] - handleMinMax[0]; + } + if (delta > 0 && handleMinMax[1] + delta > extent[1]) { + delta = extent[1] - handleMinMax[1]; + } + return delta; + } + }; + + +/***/ }, +/* 325 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + module.exports = __webpack_require__(318).extend({ + + type: 'dataZoom.inside', + + /** + * @protected + */ + defaultOption: { + zoomLock: false // Whether disable zoom but only pan. + } + }); + + +/***/ }, +/* 326 */ +/***/ function(module, exports, __webpack_require__) { + + + + var DataZoomView = __webpack_require__(321); + var zrUtil = __webpack_require__(4); + var sliderMove = __webpack_require__(324); + var roams = __webpack_require__(327); + var bind = zrUtil.bind; + + var InsideZoomView = DataZoomView.extend({ + + type: 'dataZoom.inside', + + /** + * @override + */ + init: function (ecModel, api) { + /** + * 'throttle' is used in this.dispatchAction, so we save range + * to avoid missing some 'pan' info. + * @private + * @type {Array.} + */ + this._range; + }, + + /** + * @override + */ + render: function (dataZoomModel, ecModel, api, payload) { + InsideZoomView.superApply(this, 'render', arguments); + + // Notice: origin this._range should be maintained, and should not be re-fetched + // from dataZoomModel when payload.type is 'dataZoom', otherwise 'pan' or 'zoom' + // info will be missed because of 'throttle' of this.dispatchAction. + if (roams.shouldRecordRange(payload, dataZoomModel.id)) { + this._range = dataZoomModel.getPercentRange(); + } + + // Reset controllers. + var coordInfoList = this.getTargetInfo().cartesians; + var allCoordIds = zrUtil.map(coordInfoList, function (coordInfo) { + return roams.generateCoordId(coordInfo.model); + }); + + zrUtil.each(coordInfoList, function (coordInfo) { + var coordModel = coordInfo.model; + roams.register( + api, + { + coordId: roams.generateCoordId(coordModel), + allCoordIds: allCoordIds, + coordinateSystem: coordModel.coordinateSystem, + dataZoomId: dataZoomModel.id, + throttleRate: dataZoomModel.get('throttle', true), + panGetRange: bind(this._onPan, this, coordInfo), + zoomGetRange: bind(this._onZoom, this, coordInfo) + } + ); + }, this); + + // TODO + // polar支持 + }, + + /** + * @override + */ + dispose: function () { + roams.unregister(this.api, this.dataZoomModel.id); + InsideZoomView.superApply(this, 'dispose', arguments); + this._range = null; + }, + + /** + * @private + */ + _onPan: function (coordInfo, controller, dx, dy) { + return ( + this._range = panCartesian( + [dx, dy], this._range, controller, coordInfo + ) + ); + }, + + /** + * @private + */ + _onZoom: function (coordInfo, controller, scale, mouseX, mouseY) { + var dataZoomModel = this.dataZoomModel; + + if (dataZoomModel.option.zoomLock) { + return this._range; + } + + return ( + this._range = scaleCartesian( + 1 / scale, [mouseX, mouseY], this._range, + controller, coordInfo, dataZoomModel + ) + ); + } + + }); + + function panCartesian(pixelDeltas, range, controller, coordInfo) { + range = range.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo(pixelDeltas, axisModel, controller); + + var percentDelta = directionInfo.signal + * (range[1] - range[0]) + * directionInfo.pixel / directionInfo.pixelLength; + + sliderMove( + percentDelta, + range, + [0, 100], + 'rigid' + ); + + return range; + } + + function scaleCartesian(scale, mousePoint, range, controller, coordInfo, dataZoomModel) { + range = range.slice(); + + // Calculate transform by the first axis. + var axisModel = coordInfo.axisModels[0]; + if (!axisModel) { + return; + } + + var directionInfo = getDirectionInfo(mousePoint, axisModel, controller); + + var mouse = directionInfo.pixel - directionInfo.pixelStart; + var percentPoint = mouse / directionInfo.pixelLength * (range[1] - range[0]) + range[0]; + + scale = Math.max(scale, 0); + range[0] = (range[0] - percentPoint) * scale + percentPoint; + range[1] = (range[1] - percentPoint) * scale + percentPoint; + + return fixRange(range); + } + + function getDirectionInfo(xy, axisModel, controller) { + var axis = axisModel.axis; + var rect = controller.rectProvider(); + var ret = {}; + + if (axis.dim === 'x') { + ret.pixel = xy[0]; + ret.pixelLength = rect.width; + ret.pixelStart = rect.x; + ret.signal = axis.inverse ? 1 : -1; + } + else { // axis.dim === 'y' + ret.pixel = xy[1]; + ret.pixelLength = rect.height; + ret.pixelStart = rect.y; + ret.signal = axis.inverse ? -1 : 1; + } + + return ret; + } + + function fixRange(range) { + // Clamp, using !(<= or >=) to handle NaN. + // jshint ignore:start + var bound = [0, 100]; + !(range[0] <= bound[1]) && (range[0] = bound[1]); + !(range[1] <= bound[1]) && (range[1] = bound[1]); + !(range[0] >= bound[0]) && (range[0] = bound[0]); + !(range[1] >= bound[0]) && (range[1] = bound[0]); + // jshint ignore:end + + return range; + } + + module.exports = InsideZoomView; + + +/***/ }, +/* 327 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Roam controller manager. + */ + + + // Only create one roam controller for each coordinate system. + // one roam controller might be refered by two inside data zoom + // components (for example, one for x and one for y). When user + // pan or zoom, only dispatch one action for those data zoom + // components. + + var zrUtil = __webpack_require__(4); + var RoamController = __webpack_require__(174); + var throttle = __webpack_require__(308); + var curry = zrUtil.curry; + + var ATTR = '\0_ec_dataZoom_roams'; + + var roams = { + + /** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {Object} dataZoomInfo + * @param {string} dataZoomInfo.coordId + * @param {Object} dataZoomInfo.coordinateSystem + * @param {Array.} dataZoomInfo.allCoordIds + * @param {string} dataZoomInfo.dataZoomId + * @param {number} dataZoomInfo.throttleRate + * @param {Function} dataZoomInfo.panGetRange + * @param {Function} dataZoomInfo.zoomGetRange + */ + register: function (api, dataZoomInfo) { + var store = giveStore(api); + var theDataZoomId = dataZoomInfo.dataZoomId; + var theCoordId = dataZoomInfo.coordId; + + // Do clean when a dataZoom changes its target coordnate system. + zrUtil.each(store, function (record, coordId) { + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[theDataZoomId] + && zrUtil.indexOf(dataZoomInfo.allCoordIds, theCoordId) < 0 + ) { + delete dataZoomInfos[theDataZoomId]; + record.count--; + } + }); + + cleanStore(store); + + var record = store[theCoordId]; + + // Create if needed. + if (!record) { + record = store[theCoordId] = { + coordId: theCoordId, + dataZoomInfos: {}, + count: 0 + }; + record.controller = createController(api, dataZoomInfo, record); + record.dispatchAction = zrUtil.curry(dispatchAction, api); + } + + // Consider resize, area should be always updated. + var rect = dataZoomInfo.coordinateSystem.getRect().clone(); + record.controller.rectProvider = function () { + return rect; + }; + + // Update throttle. + throttle.createOrUpdate( + record, + 'dispatchAction', + dataZoomInfo.throttleRate, + 'fixRate' + ); + + // Update reference of dataZoom. + !(record.dataZoomInfos[theDataZoomId]) && record.count++; + record.dataZoomInfos[theDataZoomId] = dataZoomInfo; + }, + + /** + * @public + * @param {module:echarts/ExtensionAPI} api + * @param {string} dataZoomId + */ + unregister: function (api, dataZoomId) { + var store = giveStore(api); + + zrUtil.each(store, function (record) { + var dataZoomInfos = record.dataZoomInfos; + if (dataZoomInfos[dataZoomId]) { + delete dataZoomInfos[dataZoomId]; + record.count--; + } + }); + + cleanStore(store); + }, + + /** + * @public + */ + shouldRecordRange: function (payload, dataZoomId) { + if (payload && payload.type === 'dataZoom' && payload.batch) { + for (var i = 0, len = payload.batch.length; i < len; i++) { + if (payload.batch[i].dataZoomId === dataZoomId) { + return false; + } + } + } + return true; + }, + + /** + * @public + */ + generateCoordId: function (coordModel) { + return coordModel.type + '\0_' + coordModel.id; + } + }; + + /** + * Key: coordId, value: {dataZoomInfos: [], count, controller} + * @type {Array.} + */ + function giveStore(api) { + // Mount store on zrender instance, so that we do not + // need to worry about dispose. + var zr = api.getZr(); + return zr[ATTR] || (zr[ATTR] = {}); + } + + function createController(api, dataZoomInfo, newRecord) { + var controller = new RoamController(api.getZr()); + controller.enable(); + controller.on('pan', curry(onPan, newRecord)); + controller.on('zoom', curry(onZoom, newRecord)); + + return controller; + } + + function cleanStore(store) { + zrUtil.each(store, function (record, coordId) { + if (!record.count) { + record.controller.off('pan').off('zoom'); + delete store[coordId]; + } + }); + } + + function onPan(record, dx, dy) { + wrapAndDispatch(record, function (info) { + return info.panGetRange(record.controller, dx, dy); + }); + } + + function onZoom(record, scale, mouseX, mouseY) { + wrapAndDispatch(record, function (info) { + return info.zoomGetRange(record.controller, scale, mouseX, mouseY); + }); + } + + function wrapAndDispatch(record, getRange) { + var batch = []; + + zrUtil.each(record.dataZoomInfos, function (info) { + var range = getRange(info); + range && batch.push({ + dataZoomId: info.dataZoomId, + start: range[0], + end: range[1] + }); + }); + + record.dispatchAction(batch); + } + + /** + * This action will be throttled. + */ + function dispatchAction(api, batch) { + api.dispatchAction({ + type: 'dataZoom', + batch: batch + }); + } + + module.exports = roams; + + + +/***/ }, +/* 328 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom processor + */ + + + var echarts = __webpack_require__(1); + + echarts.registerProcessor(function (ecModel, api) { + + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // We calculate window and reset axis here but not in model + // init stage and not after action dispatch handler, because + // reset should be called after seriesData.restoreData. + dataZoomModel.eachTargetAxis(resetSingleAxis); + + // Caution: data zoom filtering is order sensitive when using + // percent range and no min/max/scale set on axis. + // For example, we have dataZoom definition: + // [ + // {xAxisIndex: 0, start: 30, end: 70}, + // {yAxisIndex: 0, start: 20, end: 80} + // ] + // In this case, [20, 80] of y-dataZoom should be based on data + // that have filtered by x-dataZoom using range of [30, 70], + // but should not be based on full raw data. Thus sliding + // x-dataZoom will change both ranges of xAxis and yAxis, + // while sliding y-dataZoom will only change the range of yAxis. + // So we should filter x-axis after reset x-axis immediately, + // and then reset y-axis and filter y-axis. + dataZoomModel.eachTargetAxis(filterSingleAxis); + }); + + ecModel.eachComponent('dataZoom', function (dataZoomModel) { + // Fullfill all of the range props so that user + // is able to get them from chart.getOption(). + var axisProxy = dataZoomModel.findRepresentativeAxisProxy(); + var percentRange = axisProxy.getDataPercentWindow(); + var valueRange = axisProxy.getDataValueWindow(); + + dataZoomModel.setRawRange({ + start: percentRange[0], + end: percentRange[1], + startValue: valueRange[0], + endValue: valueRange[1] + }); + }); + }); + + function resetSingleAxis(dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).reset(dataZoomModel); + } + + function filterSingleAxis(dimNames, axisIndex, dataZoomModel) { + dataZoomModel.getAxisProxy(dimNames.name, axisIndex).filterData(dataZoomModel); + } + + + + +/***/ }, +/* 329 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom action + */ + + + var zrUtil = __webpack_require__(4); + var helper = __webpack_require__(319); + var echarts = __webpack_require__(1); + + + echarts.registerAction('dataZoom', function (payload, ecModel) { + + var linkedNodesFinder = helper.createLinkedNodesFinder( + zrUtil.bind(ecModel.eachComponent, ecModel, 'dataZoom'), + helper.eachAxisDim, + function (model, dimNames) { + return model.get(dimNames.axisIndex); + } + ); + + var effectedModels = []; + + ecModel.eachComponent( + {mainType: 'dataZoom', query: payload}, + function (model, index) { + effectedModels.push.apply( + effectedModels, linkedNodesFinder(model).nodes + ); + } + ); + + zrUtil.each(effectedModels, function (dataZoomModel, index) { + dataZoomModel.setRawRange({ + start: payload.start, + end: payload.end, + startValue: payload.startValue, + endValue: payload.endValue + }); + }); + + }); + + + +/***/ }, +/* 330 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * visualMap component entry + */ + + + __webpack_require__(331); + __webpack_require__(342); + + + +/***/ }, +/* 331 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(332) + ); + + __webpack_require__(333); + __webpack_require__(334); + __webpack_require__(335); + __webpack_require__(338); + __webpack_require__(341); + + + +/***/ }, +/* 332 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file VisualMap preprocessor + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + module.exports = function (option) { + var visualMap = option && option.visualMap; + + if (!zrUtil.isArray(visualMap)) { + visualMap = visualMap ? [visualMap] : []; + } + + each(visualMap, function (opt) { + if (!opt) { + return; + } + + // rename splitList to pieces + if (has(opt, 'splitList') && !has(opt, 'pieces')) { + opt.pieces = opt.splitList; + delete opt.splitList; + } + + var pieces = opt.pieces; + if (pieces && zrUtil.isArray(pieces)) { + each(pieces, function (piece) { + if (zrUtil.isObject(piece)) { + if (has(piece, 'start') && !has(piece, 'min')) { + piece.min = piece.start; + } + if (has(piece, 'end') && !has(piece, 'max')) { + piece.max = piece.end; + } + } + }); + } + }); + }; + + function has(obj, name) { + return obj && obj.hasOwnProperty && obj.hasOwnProperty(name); + } + + + +/***/ }, +/* 333 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('visualMap', function (option) { + // Compatible with ec2, when splitNumber === 0, continuous visualMap will be used. + return ( + !option.categories + && ( + !( + option.pieces + ? option.pieces.length > 0 + : option.splitNumber > 0 + ) + || option.calculable + ) + ) + ? 'continuous' : 'piecewise'; + }); + + + +/***/ }, +/* 334 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data range visual coding. + */ + + + var echarts = __webpack_require__(1); + var visualSolution = __webpack_require__(306); + var VisualMapping = __webpack_require__(192); + + echarts.registerVisual(echarts.PRIORITY.VISUAL.COMPONENT, function (ecModel) { + ecModel.eachComponent('visualMap', function (visualMapModel) { + processSingleVisualMap(visualMapModel, ecModel); + }); + + prepareVisualMeta(ecModel); + }); + + function processSingleVisualMap(visualMapModel, ecModel) { + visualMapModel.eachTargetSeries(function (seriesModel) { + var data = seriesModel.getData(); + + visualSolution.applyVisual( + visualMapModel.stateList, + visualMapModel.targetVisuals, + data, + visualMapModel.getValueState, + visualMapModel, + visualMapModel.getDataDimension(data) + ); + }); + } + + // Only support color. + function prepareVisualMeta(ecModel) { + ecModel.eachSeries(function (seriesModel) { + var data = seriesModel.getData(); + var visualMetaList = []; + + ecModel.eachComponent('visualMap', function (visualMapModel) { + if (visualMapModel.isTargetSeries(seriesModel)) { + var visualMeta = {}; + visualMetaList.push(visualMeta); + visualMeta.stops = visualMapModel.getStops(seriesModel, getColorVisual); + visualMeta.dimension = visualMapModel.getDataDimension(data); + } + }); + + // console.log(JSON.stringify(visualMetaList.map(a => a.stops))); + seriesModel.getData().setVisual('visualMeta', visualMetaList); + }); + } + + // FIXME + // performance and export for heatmap? + function getColorVisual(visualMapModel, value, valueState) { + var mappings = visualMapModel.targetVisuals[valueState]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + var resultVisual = {}; + + for (var i = 0, len = visualTypes.length; i < len; i++) { + var type = visualTypes[i]; + var mapping = mappings[ + type === 'colorAlpha' ? '__alphaForOpacity' : type + ]; + mapping && mapping.applyVisual(value, getVisual, setVisual); + } + + return resultVisual.color; + + function getVisual(key) { + return resultVisual[key]; + } + + function setVisual(key, value) { + resultVisual[key] = value; + } + } + + + + +/***/ }, +/* 335 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var VisualMapModel = __webpack_require__(336); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + // Constant + var DEFAULT_BAR_BOUND = [20, 140]; + + var ContinuousModel = VisualMapModel.extend({ + + type: 'visualMap.continuous', + + /** + * @protected + */ + defaultOption: { + align: 'auto', // 'auto', 'left', 'right', 'top', 'bottom' + calculable: false, // This prop effect default component type determine, + // See echarts/component/visualMap/typeDefaulter. + range: null, // selected range. In default case `range` is [min, max] + // and can auto change along with modification of min max, + // util use specifid a range. + realtime: true, // Whether realtime update. + itemHeight: null, // The length of the range control edge. + itemWidth: null, // The length of the other side. + hoverLink: true, // Enable hover highlight. + hoverLinkDataSize: null,// The size of hovered data. + hoverLinkOnHandle: true // Whether trigger hoverLink when hover handle. + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + ContinuousModel.superApply(this, 'optionUpdated', arguments); + + this.resetTargetSeries(); + this.resetExtent(); + + this.resetVisual(function (mappingOption) { + mappingOption.mappingMethod = 'linear'; + mappingOption.dataExtent = this.getExtent(); + }); + + this._resetRange(); + }, + + /** + * @protected + * @override + */ + resetItemSize: function () { + ContinuousModel.superApply(this, 'resetItemSize', arguments); + + var itemSize = this.itemSize; + + this._orient === 'horizontal' && itemSize.reverse(); + + (itemSize[0] == null || isNaN(itemSize[0])) && (itemSize[0] = DEFAULT_BAR_BOUND[0]); + (itemSize[1] == null || isNaN(itemSize[1])) && (itemSize[1] = DEFAULT_BAR_BOUND[1]); + }, + + /** + * @private + */ + _resetRange: function () { + var dataExtent = this.getExtent(); + var range = this.option.range; + + if (!range || range.auto) { + // `range` should always be array (so we dont use other + // value like 'auto') for user-friend. (consider getOption). + dataExtent.auto = 1; + this.option.range = dataExtent; + } + else if (zrUtil.isArray(range)) { + if (range[0] > range[1]) { + range.reverse(); + } + range[0] = Math.max(range[0], dataExtent[0]); + range[1] = Math.min(range[1], dataExtent[1]); + } + }, + + /** + * @protected + * @override + */ + completeVisualOption: function () { + VisualMapModel.prototype.completeVisualOption.apply(this, arguments); + + zrUtil.each(this.stateList, function (state) { + var symbolSize = this.option.controller[state].symbolSize; + if (symbolSize && symbolSize[0] !== symbolSize[1]) { + symbolSize[0] = 0; // For good looking. + } + }, this); + }, + + /** + * @override + */ + setSelected: function (selected) { + this.option.range = selected.slice(); + this._resetRange(); + }, + + /** + * @public + */ + getSelected: function () { + var dataExtent = this.getExtent(); + + var dataInterval = numberUtil.asc( + (this.get('range') || []).slice() + ); + + // Clamp + dataInterval[0] > dataExtent[1] && (dataInterval[0] = dataExtent[1]); + dataInterval[1] > dataExtent[1] && (dataInterval[1] = dataExtent[1]); + dataInterval[0] < dataExtent[0] && (dataInterval[0] = dataExtent[0]); + dataInterval[1] < dataExtent[0] && (dataInterval[1] = dataExtent[0]); + + return dataInterval; + }, + + /** + * @override + */ + getValueState: function (value) { + var range = this.option.range; + var dataExtent = this.getExtent(); + + // When range[0] === dataExtent[0], any value larger than dataExtent[0] maps to 'inRange'. + // range[1] is processed likewise. + return ( + (range[0] <= dataExtent[0] || range[0] <= value) + && (range[1] >= dataExtent[1] || value <= range[1]) + ) ? 'inRange' : 'outOfRange'; + }, + + /** + * @params {Array.} range target value: range[0] <= value && value <= range[1] + * @return {Array.} [{seriesId, dataIndices: >}, ...] + */ + findTargetDataIndices: function (range) { + var result = []; + + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + + data.each(this.getDataDimension(data), function (value, dataIndex) { + range[0] <= value && value <= range[1] && dataIndices.push(dataIndex); + }, true, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + getStops: function (seriesModel, getColorVisual) { + var result = []; + insertStopList(this, 'outOfRange', this.getExtent(), result); + insertStopList(this, 'inRange', this.option.range.slice(), result); + + zrUtil.each(result, function (item) { + item.color = getColorVisual(this, item.value, item.valueState); + }, this); + + return result; + } + + }); + + function getColorStopValues(visualMapModel, valueState, dataExtent) { + var mapping = visualMapModel.targetVisuals[valueState].color; + + if (!mapping) { + return dataExtent.slice(); + } + + var count = mapping.option.visual.length; + + if (count <= 1 || dataExtent[0] === dataExtent[1]) { + return dataExtent.slice(); + } + + // We only use linear mappping for color, so we can do inverse mapping: + var step = (dataExtent[1] - dataExtent[0]) / (count - 1); + var value = dataExtent[0]; + var stopValues = []; + for (var i = 0; i < count && value < dataExtent[1]; i++) { + stopValues.push(value); + value += step; + } + stopValues.push(dataExtent[1]); + + return stopValues; + } + + function insertStopList(visualMapModel, valueState, dataExtent, result) { + var stops = getColorStopValues(visualMapModel, valueState, dataExtent); + + zrUtil.each(stops, function (val) { + var stop = {value: val, valueState: valueState}; + var inRange = 0; + for (var i = 0; i < result.length; i++) { + // Format to: outOfRange -- inRange -- outOfRange. + inRange |= result[i].valueState === 'inRange'; + if (val < result[i].value) { + result.splice(i, 0, stop); + return; + } + inRange && (result[i].valueState = 'inRange'); + } + result.push(stop); + }); + } + + module.exports = ContinuousModel; + + + +/***/ }, +/* 336 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Controller visual map model + */ + + + var echarts = __webpack_require__(1); + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + var visualDefault = __webpack_require__(337); + var VisualMapping = __webpack_require__(192); + var visualSolution = __webpack_require__(306); + var mapVisual = VisualMapping.mapVisual; + var modelUtil = __webpack_require__(5); + var eachVisual = VisualMapping.eachVisual; + var numberUtil = __webpack_require__(7); + var isArray = zrUtil.isArray; + var each = zrUtil.each; + var asc = numberUtil.asc; + var linearMap = numberUtil.linearMap; + var noop = zrUtil.noop; + + var DEFAULT_COLOR = ['#f6efa6', '#d88273', '#bf444c']; + + var VisualMapModel = echarts.extendComponentModel({ + + type: 'visualMap', + + dependencies: ['series'], + + /** + * @readOnly + * @type {Array.} + */ + stateList: ['inRange', 'outOfRange'], + + /** + * @readOnly + * @type {Array.} + */ + replacableOptionKeys: [ + 'inRange', 'outOfRange', 'target', 'controller', 'color' + ], + + /** + * [lowerBound, upperBound] + * + * @readOnly + * @type {Array.} + */ + dataBound: [-Infinity, Infinity], + + /** + * @readOnly + * @type {string|Object} + */ + layoutMode: {type: 'box', ignoreSize: true}, + + /** + * @protected + */ + defaultOption: { + show: true, + + zlevel: 0, + z: 4, + + seriesIndex: null, // 所控制的series indices,默认所有有value的series. + + // set min: 0, max: 200, only for campatible with ec2. + // In fact min max should not have default value. + min: 0, // min value, must specified if pieces is not specified. + max: 200, // max value, must specified if pieces is not specified. + + dimension: null, + inRange: null, // 'color', 'colorHue', 'colorSaturation', 'colorLightness', 'colorAlpha', + // 'symbol', 'symbolSize' + outOfRange: null, // 'color', 'colorHue', 'colorSaturation', + // 'colorLightness', 'colorAlpha', + // 'symbol', 'symbolSize' + + left: 0, // 'center' ¦ 'left' ¦ 'right' ¦ {number} (px) + right: null, // The same as left. + top: null, // 'top' ¦ 'bottom' ¦ 'center' ¦ {number} (px) + bottom: 0, // The same as top. + + itemWidth: null, + itemHeight: null, + inverse: false, + orient: 'vertical', // 'horizontal' ¦ 'vertical' + + backgroundColor: 'rgba(0,0,0,0)', + borderColor: '#ccc', // 值域边框颜色 + contentColor: '#5793f3', + inactiveColor: '#aaa', + borderWidth: 0, // 值域边框线宽,单位px,默认为0(无边框) + padding: 5, // 值域内边距,单位px,默认各方向内边距为5, + // 接受数组分别设定上右下左边距,同css + textGap: 10, // + precision: 0, // 小数精度,默认为0,无小数点 + color: null, //颜色(deprecated,兼容ec2,顺序同pieces,不同于inRange/outOfRange) + + formatter: null, + text: null, // 文本,如['高', '低'],兼容ec2,text[0]对应高值,text[1]对应低值 + textStyle: { + color: '#333' // 值域文字颜色 + } + }, + + /** + * @protected + */ + init: function (option, parentModel, ecModel) { + + /** + * @private + * @type {Array.} + */ + this._dataExtent; + + /** + * @readOnly + */ + this.targetVisuals = {}; + + /** + * @readOnly + */ + this.controllerVisuals = {}; + + /** + * @readOnly + */ + this.textStyleModel; + + /** + * [width, height] + * @readOnly + * @type {Array.} + */ + this.itemSize; + + this.mergeDefaultAndTheme(option, ecModel); + }, + + /** + * @protected + */ + optionUpdated: function (newOption, isInit) { + var thisOption = this.option; + + // FIXME + // necessary? + // Disable realtime view update if canvas is not supported. + if (!env.canvasSupported) { + thisOption.realtime = false; + } + + !isInit && visualSolution.replaceVisualOption( + thisOption, newOption, this.replacableOptionKeys + ); + + this.textStyleModel = this.getModel('textStyle'); + + this.resetItemSize(); + + this.completeVisualOption(); + }, + + /** + * @protected + */ + resetVisual: function (supplementVisualOption) { + var stateList = this.stateList; + supplementVisualOption = zrUtil.bind(supplementVisualOption, this); + + this.controllerVisuals = visualSolution.createVisualMappings( + this.option.controller, stateList, supplementVisualOption + ); + this.targetVisuals = visualSolution.createVisualMappings( + this.option.target, stateList, supplementVisualOption + ); + }, + + + /** + * @protected + */ + resetTargetSeries: function () { + var thisOption = this.option; + var allSeriesIndex = thisOption.seriesIndex == null; + thisOption.seriesIndex = allSeriesIndex + ? [] : modelUtil.normalizeToArray(thisOption.seriesIndex); + + allSeriesIndex && this.ecModel.eachSeries(function (seriesModel, index) { + thisOption.seriesIndex.push(index); + }); + }, + + /** + * @public + */ + eachTargetSeries: function (callback, context) { + zrUtil.each(this.option.seriesIndex, function (seriesIndex) { + callback.call(context, this.ecModel.getSeriesByIndex(seriesIndex)); + }, this); + }, + + /** + * @pubilc + */ + isTargetSeries: function (seriesModel) { + var is = false; + this.eachTargetSeries(function (model) { + model === seriesModel && (is = true); + }); + return is; + }, + + /** + * @example + * this.formatValueText(someVal); // format single numeric value to text. + * this.formatValueText(someVal, true); // format single category value to text. + * this.formatValueText([min, max]); // format numeric min-max to text. + * this.formatValueText([this.dataBound[0], max]); // using data lower bound. + * this.formatValueText([min, this.dataBound[1]]); // using data upper bound. + * + * @param {number|Array.} value Real value, or this.dataBound[0 or 1]. + * @param {boolean} [isCategory=false] Only available when value is number. + * @param {Array.} edgeSymbols Open-close symbol when value is interval. + * @return {string} + * @protected + */ + formatValueText: function(value, isCategory, edgeSymbols) { + var option = this.option; + var precision = option.precision; + var dataBound = this.dataBound; + var formatter = option.formatter; + var isMinMax; + var textValue; + edgeSymbols = edgeSymbols || ['<', '>']; + + if (zrUtil.isArray(value)) { + value = value.slice(); + isMinMax = true; + } + + textValue = isCategory + ? value + : (isMinMax + ? [toFixed(value[0]), toFixed(value[1])] + : toFixed(value) + ); + + if (zrUtil.isString(formatter)) { + return formatter + .replace('{value}', isMinMax ? textValue[0] : textValue) + .replace('{value2}', isMinMax ? textValue[1] : textValue); + } + else if (zrUtil.isFunction(formatter)) { + return isMinMax + ? formatter(value[0], value[1]) + : formatter(value); + } + + if (isMinMax) { + if (value[0] === dataBound[0]) { + return edgeSymbols[0] + ' ' + textValue[1]; + } + else if (value[1] === dataBound[1]) { + return edgeSymbols[1] + ' ' + textValue[0]; + } + else { + return textValue[0] + ' - ' + textValue[1]; + } + } + else { // Format single value (includes category case). + return textValue; + } + + function toFixed(val) { + return val === dataBound[0] + ? 'min' + : val === dataBound[1] + ? 'max' + : (+val).toFixed(precision); + } + }, + + /** + * @protected + */ + resetExtent: function () { + var thisOption = this.option; + + // Can not calculate data extent by data here. + // Because series and data may be modified in processing stage. + // So we do not support the feature "auto min/max". + + var extent = asc([thisOption.min, thisOption.max]); + + this._dataExtent = extent; + }, + + /** + * @public + * @param {module:echarts/data/List} list + * @return {string} Concrete dimention. If return null/undefined, + * no dimension used. + */ + getDataDimension: function (list) { + var optDim = this.option.dimension; + return optDim != null + ? optDim : list.dimensions.length - 1; + }, + + /** + * @public + * @override + */ + getExtent: function () { + return this._dataExtent.slice(); + }, + + /** + * @protected + */ + completeVisualOption: function () { + var thisOption = this.option; + var base = {inRange: thisOption.inRange, outOfRange: thisOption.outOfRange}; + + var target = thisOption.target || (thisOption.target = {}); + var controller = thisOption.controller || (thisOption.controller = {}); + + zrUtil.merge(target, base); // Do not override + zrUtil.merge(controller, base); // Do not override + + var isCategory = this.isCategory(); + + completeSingle.call(this, target); + completeSingle.call(this, controller); + completeInactive.call(this, target, 'inRange', 'outOfRange'); + // completeInactive.call(this, target, 'outOfRange', 'inRange'); + completeController.call(this, controller); + + function completeSingle(base) { + // Compatible with ec2 dataRange.color. + // The mapping order of dataRange.color is: [high value, ..., low value] + // whereas inRange.color and outOfRange.color is [low value, ..., high value] + // Notice: ec2 has no inverse. + if (isArray(thisOption.color) + // If there has been inRange: {symbol: ...}, adding color is a mistake. + // So adding color only when no inRange defined. + && !base.inRange + ) { + base.inRange = {color: thisOption.color.slice().reverse()}; + } + + // Compatible with previous logic, always give a defautl color, otherwise + // simple config with no inRange and outOfRange will not work. + // Originally we use visualMap.color as the default color, but setOption at + // the second time the default color will be erased. So we change to use + // constant DEFAULT_COLOR. + // If user do not want the defualt color, set inRange: {color: null}. + base.inRange = base.inRange || {color: DEFAULT_COLOR}; + + // If using shortcut like: {inRange: 'symbol'}, complete default value. + each(this.stateList, function (state) { + var visualType = base[state]; + + if (zrUtil.isString(visualType)) { + var defa = visualDefault.get(visualType, 'active', isCategory); + if (defa) { + base[state] = {}; + base[state][visualType] = defa; + } + else { + // Mark as not specified. + delete base[state]; + } + } + }, this); + } + + function completeInactive(base, stateExist, stateAbsent) { + var optExist = base[stateExist]; + var optAbsent = base[stateAbsent]; + + if (optExist && !optAbsent) { + optAbsent = base[stateAbsent] = {}; + each(optExist, function (visualData, visualType) { + if (!VisualMapping.isValidType(visualType)) { + return; + } + + var defa = visualDefault.get(visualType, 'inactive', isCategory); + + if (defa != null) { + optAbsent[visualType] = defa; + + // Compatibable with ec2: + // Only inactive color to rgba(0,0,0,0) can not + // make label transparent, so use opacity also. + if (visualType === 'color' + && !optAbsent.hasOwnProperty('opacity') + && !optAbsent.hasOwnProperty('colorAlpha') + ) { + optAbsent.opacity = [0, 0]; + } + } + }); + } + } + + function completeController(controller) { + var symbolExists = (controller.inRange || {}).symbol + || (controller.outOfRange || {}).symbol; + var symbolSizeExists = (controller.inRange || {}).symbolSize + || (controller.outOfRange || {}).symbolSize; + var inactiveColor = this.get('inactiveColor'); + + each(this.stateList, function (state) { + + var itemSize = this.itemSize; + var visuals = controller[state]; + + // Set inactive color for controller if no other color + // attr (like colorAlpha) specified. + if (!visuals) { + visuals = controller[state] = { + color: isCategory ? inactiveColor : [inactiveColor] + }; + } + + // Consistent symbol and symbolSize if not specified. + if (visuals.symbol == null) { + visuals.symbol = symbolExists + && zrUtil.clone(symbolExists) + || (isCategory ? 'roundRect' : ['roundRect']); + } + if (visuals.symbolSize == null) { + visuals.symbolSize = symbolSizeExists + && zrUtil.clone(symbolSizeExists) + || (isCategory ? itemSize[0] : [itemSize[0], itemSize[0]]); + } + + // Filter square and none. + visuals.symbol = mapVisual(visuals.symbol, function (symbol) { + return (symbol === 'none' || symbol === 'square') ? 'roundRect' : symbol; + }); + + // Normalize symbolSize + var symbolSize = visuals.symbolSize; + + if (symbolSize != null) { + var max = -Infinity; + // symbolSize can be object when categories defined. + eachVisual(symbolSize, function (value) { + value > max && (max = value); + }); + visuals.symbolSize = mapVisual(symbolSize, function (value) { + return linearMap(value, [0, max], [0, itemSize[0]], true); + }); + } + + }, this); + } + }, + + /** + * @protected + */ + resetItemSize: function () { + this.itemSize = [ + parseFloat(this.get('itemWidth')), + parseFloat(this.get('itemHeight')) + ]; + }, + + /** + * @public + */ + isCategory: function () { + return !!this.option.categories; + }, + + /** + * @public + * @abstract + */ + setSelected: noop, + + /** + * @public + * @abstract + * @param {*|module:echarts/data/List} valueOrData + * @param {number} dataIndex + * @return {string} state See this.stateList + */ + getValueState: noop + + }); + + module.exports = VisualMapModel; + + + +/***/ }, +/* 337 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Visual mapping. + */ + + + var zrUtil = __webpack_require__(4); + + var visualDefault = { + + /** + * @public + */ + get: function (visualType, key, isCategory) { + var value = zrUtil.clone( + (defaultOption[visualType] || {})[key] + ); + + return isCategory + ? (zrUtil.isArray(value) ? value[value.length - 1] : value) + : value; + } + + }; + + var defaultOption = { + + color: { + active: ['#006edd', '#e0ffff'], + inactive: ['rgba(0,0,0,0)'] + }, + + colorHue: { + active: [0, 360], + inactive: [0, 0] + }, + + colorSaturation: { + active: [0.3, 1], + inactive: [0, 0] + }, + + colorLightness: { + active: [0.9, 0.5], + inactive: [0, 0] + }, + + colorAlpha: { + active: [0.3, 1], + inactive: [0, 0] + }, + + opacity: { + active: [0.3, 1], + inactive: [0, 0] + }, + + symbol: { + active: ['circle', 'roundRect', 'diamond'], + inactive: ['none'] + }, + + symbolSize: { + active: [10, 50], + inactive: [0, 0] + } + }; + + module.exports = visualDefault; + + + + +/***/ }, +/* 338 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapView = __webpack_require__(339); + var graphic = __webpack_require__(43); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var sliderMove = __webpack_require__(324); + var LinearGradient = __webpack_require__(79); + var helper = __webpack_require__(340); + var modelUtil = __webpack_require__(5); + + var linearMap = numberUtil.linearMap; + var each = zrUtil.each; + var mathMin = Math.min; + var mathMax = Math.max; + + // Arbitrary value + var HOVER_LINK_SIZE = 12; + var HOVER_LINK_OUT = 6; + + // Notice: + // Any "interval" should be by the order of [low, high]. + // "handle0" (handleIndex === 0) maps to + // low data value: this._dataInterval[0] and has low coord. + // "handle1" (handleIndex === 1) maps to + // high data value: this._dataInterval[1] and has high coord. + // The logic of transform is implemented in this._createBarGroup. + + var ContinuousView = VisualMapView.extend({ + + type: 'visualMap.continuous', + + /** + * @override + */ + init: function () { + + ContinuousView.superApply(this, 'init', arguments); + + /** + * @private + */ + this._shapes = {}; + + /** + * @private + */ + this._dataInterval = []; + + /** + * @private + */ + this._handleEnds = []; + + /** + * @private + */ + this._orient; + + /** + * @private + */ + this._useHandle; + + /** + * @private + */ + this._hoverLinkDataIndices = []; + + /** + * @private + */ + this._dragging; + + /** + * @private + */ + this._hovering; + }, + + /** + * @protected + * @override + */ + doRender: function (visualMapModel, ecModel, api, payload) { + if (!payload || payload.type !== 'selectDataRange' || payload.from !== this.uid) { + this._buildView(); + } + }, + + /** + * @private + */ + _buildView: function () { + this.group.removeAll(); + + var visualMapModel = this.visualMapModel; + var thisGroup = this.group; + + this._orient = visualMapModel.get('orient'); + this._useHandle = visualMapModel.get('calculable'); + + this._resetInterval(); + + this._renderBar(thisGroup); + + var dataRangeText = visualMapModel.get('text'); + this._renderEndsText(thisGroup, dataRangeText, 0); + this._renderEndsText(thisGroup, dataRangeText, 1); + + // Do this for background size calculation. + this._updateView(true); + + // After updating view, inner shapes is built completely, + // and then background can be rendered. + this.renderBackground(thisGroup); + + // Real update view + this._updateView(); + + this._enableHoverLinkToSeries(); + this._enableHoverLinkFromSeries(); + + this.positionGroup(thisGroup); + }, + + /** + * @private + */ + _renderEndsText: function (group, dataRangeText, endsIndex) { + if (!dataRangeText) { + return; + } + + // Compatible with ec2, text[0] map to high value, text[1] map low value. + var text = dataRangeText[1 - endsIndex]; + text = text != null ? text + '' : ''; + + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var itemSize = visualMapModel.itemSize; + + var barGroup = this._shapes.barGroup; + var position = this._applyTransform( + [ + itemSize[0] / 2, + endsIndex === 0 ? -textGap : itemSize[1] + textGap + ], + barGroup + ); + var align = this._applyTransform( + endsIndex === 0 ? 'bottom' : 'top', + barGroup + ); + var orient = this._orient; + var textStyleModel = this.visualMapModel.textStyleModel; + + this.group.add(new graphic.Text({ + style: { + x: position[0], + y: position[1], + textVerticalAlign: orient === 'horizontal' ? 'middle' : align, + textAlign: orient === 'horizontal' ? align : 'center', + text: text, + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + })); + }, + + /** + * @private + */ + _renderBar: function (targetGroup) { + var visualMapModel = this.visualMapModel; + var shapes = this._shapes; + var itemSize = visualMapModel.itemSize; + var orient = this._orient; + var useHandle = this._useHandle; + var itemAlign = helper.getItemAlign(visualMapModel, this.api, itemSize); + var barGroup = shapes.barGroup = this._createBarGroup(itemAlign); + + // Bar + barGroup.add(shapes.outOfRange = createPolygon()); + barGroup.add(shapes.inRange = createPolygon( + null, + useHandle ? 'move' : null, + zrUtil.bind(this._dragHandle, this, 'all', false), + zrUtil.bind(this._dragHandle, this, 'all', true) + )); + + var textRect = visualMapModel.textStyleModel.getTextRect('国'); + var textSize = mathMax(textRect.width, textRect.height); + + // Handle + if (useHandle) { + shapes.handleThumbs = []; + shapes.handleLabels = []; + shapes.handleLabelPoints = []; + + this._createHandle(barGroup, 0, itemSize, textSize, orient, itemAlign); + this._createHandle(barGroup, 1, itemSize, textSize, orient, itemAlign); + } + + this._createIndicator(barGroup, itemSize, textSize, orient); + + targetGroup.add(barGroup); + }, + + /** + * @private + */ + _createHandle: function (barGroup, handleIndex, itemSize, textSize, orient) { + var onDrift = zrUtil.bind(this._dragHandle, this, handleIndex, false); + var onDragEnd = zrUtil.bind(this._dragHandle, this, handleIndex, true); + var handleThumb = createPolygon( + createHandlePoints(handleIndex, textSize), + 'move', + onDrift, + onDragEnd + ); + handleThumb.position[0] = itemSize[0]; + barGroup.add(handleThumb); + + // Text is always horizontal layout but should not be effected by + // transform (orient/inverse). So label is built separately but not + // use zrender/graphic/helper/RectText, and is located based on view + // group (according to handleLabelPoint) but not barGroup. + var textStyleModel = this.visualMapModel.textStyleModel; + var handleLabel = new graphic.Text({ + draggable: true, + drift: onDrift, + ondragend: onDragEnd, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + }); + this.group.add(handleLabel); + + var handleLabelPoint = [ + orient === 'horizontal' + ? textSize / 2 + : textSize * 1.5, + orient === 'horizontal' + ? (handleIndex === 0 ? -(textSize * 1.5) : (textSize * 1.5)) + : (handleIndex === 0 ? -textSize / 2 : textSize / 2) + ]; + + var shapes = this._shapes; + shapes.handleThumbs[handleIndex] = handleThumb; + shapes.handleLabelPoints[handleIndex] = handleLabelPoint; + shapes.handleLabels[handleIndex] = handleLabel; + }, + + /** + * @private + */ + _createIndicator: function (barGroup, itemSize, textSize, orient) { + var indicator = createPolygon([[0, 0]], 'move'); + indicator.position[0] = itemSize[0]; + indicator.attr({invisible: true, silent: true}); + barGroup.add(indicator); + + var textStyleModel = this.visualMapModel.textStyleModel; + var indicatorLabel = new graphic.Text({ + silent: true, + invisible: true, + style: { + x: 0, y: 0, text: '', + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + }); + this.group.add(indicatorLabel); + + var indicatorLabelPoint = [ + orient === 'horizontal' ? textSize / 2 : HOVER_LINK_OUT + 3, + 0 + ]; + + var shapes = this._shapes; + shapes.indicator = indicator; + shapes.indicatorLabel = indicatorLabel; + shapes.indicatorLabelPoint = indicatorLabelPoint; + }, + + /** + * @private + */ + _dragHandle: function (handleIndex, isEnd, dx, dy) { + if (!this._useHandle) { + return; + } + + this._dragging = !isEnd; + + if (!isEnd) { + // Transform dx, dy to bar coordination. + var vertex = this._applyTransform([dx, dy], this._shapes.barGroup, true); + this._updateInterval(handleIndex, vertex[1]); + + // Considering realtime, update view should be executed + // before dispatch action. + this._updateView(); + } + + // dragEnd do not dispatch action when realtime. + if (isEnd === !this.visualMapModel.get('realtime')) { // jshint ignore:line + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: this._dataInterval.slice() + }); + } + + if (isEnd) { + !this._hovering && this._clearHoverLinkToSeries(); + } + else if (useHoverLinkOnHandle(this.visualMapModel)) { + this._doHoverLinkToSeries(this._handleEnds[handleIndex], false); + } + }, + + /** + * @private + */ + _resetInterval: function () { + var visualMapModel = this.visualMapModel; + + var dataInterval = this._dataInterval = visualMapModel.getSelected(); + var dataExtent = visualMapModel.getExtent(); + var sizeExtent = [0, visualMapModel.itemSize[1]]; + + this._handleEnds = [ + linearMap(dataInterval[0], dataExtent, sizeExtent, true), + linearMap(dataInterval[1], dataExtent, sizeExtent, true) + ]; + }, + + /** + * @private + * @param {(number|string)} handleIndex 0 or 1 or 'all' + * @param {number} dx + * @param {number} dy + */ + _updateInterval: function (handleIndex, delta) { + delta = delta || 0; + var visualMapModel = this.visualMapModel; + var handleEnds = this._handleEnds; + + sliderMove( + delta, + handleEnds, + [0, visualMapModel.itemSize[1]], + handleIndex === 'all' ? 'rigid' : 'push', + handleIndex + ); + var dataExtent = visualMapModel.getExtent(); + var sizeExtent = [0, visualMapModel.itemSize[1]]; + // Update data interval. + this._dataInterval = [ + linearMap(handleEnds[0], sizeExtent, dataExtent, true), + linearMap(handleEnds[1], sizeExtent, dataExtent, true) + ]; + }, + + /** + * @private + */ + _updateView: function (forSketch) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var shapes = this._shapes; + + var outOfRangeHandleEnds = [0, visualMapModel.itemSize[1]]; + var inRangeHandleEnds = forSketch ? outOfRangeHandleEnds : this._handleEnds; + + var visualInRange = this._createBarVisual( + this._dataInterval, dataExtent, inRangeHandleEnds, 'inRange' + ); + var visualOutOfRange = this._createBarVisual( + dataExtent, dataExtent, outOfRangeHandleEnds, 'outOfRange' + ); + + shapes.inRange + .setStyle({ + fill: visualInRange.barColor, + opacity: visualInRange.opacity + }) + .setShape('points', visualInRange.barPoints); + shapes.outOfRange + .setStyle({ + fill: visualOutOfRange.barColor, + opacity: visualOutOfRange.opacity + }) + .setShape('points', visualOutOfRange.barPoints); + + this._updateHandle(inRangeHandleEnds, visualInRange); + }, + + /** + * @private + */ + _createBarVisual: function (dataInterval, dataExtent, handleEnds, forceState) { + var opts = { + forceState: forceState, + convertOpacityToAlpha: true + }; + var colorStops = this._makeColorGradient(dataInterval, opts); + + var symbolSizes = [ + this.getControllerVisual(dataInterval[0], 'symbolSize', opts), + this.getControllerVisual(dataInterval[1], 'symbolSize', opts) + ]; + var barPoints = this._createBarPoints(handleEnds, symbolSizes); + + return { + barColor: new LinearGradient(0, 0, 0, 1, colorStops), + barPoints: barPoints, + handlesColor: [ + colorStops[0].color, + colorStops[colorStops.length - 1].color + ] + }; + }, + + /** + * @private + */ + _makeColorGradient: function (dataInterval, opts) { + // Considering colorHue, which is not linear, so we have to sample + // to calculate gradient color stops, but not only caculate head + // and tail. + var sampleNumber = 100; // Arbitrary value. + var colorStops = []; + var step = (dataInterval[1] - dataInterval[0]) / sampleNumber; + + colorStops.push({ + color: this.getControllerVisual(dataInterval[0], 'color', opts), + offset: 0 + }); + + for (var i = 1; i < sampleNumber; i++) { + var currValue = dataInterval[0] + step * i; + if (currValue > dataInterval[1]) { + break; + } + colorStops.push({ + color: this.getControllerVisual(currValue, 'color', opts), + offset: i / sampleNumber + }); + } + + colorStops.push({ + color: this.getControllerVisual(dataInterval[1], 'color', opts), + offset: 1 + }); + + return colorStops; + }, + + /** + * @private + */ + _createBarPoints: function (handleEnds, symbolSizes) { + var itemSize = this.visualMapModel.itemSize; + + return [ + [itemSize[0] - symbolSizes[0], handleEnds[0]], + [itemSize[0], handleEnds[0]], + [itemSize[0], handleEnds[1]], + [itemSize[0] - symbolSizes[1], handleEnds[1]] + ]; + }, + + /** + * @private + */ + _createBarGroup: function (itemAlign) { + var orient = this._orient; + var inverse = this.visualMapModel.get('inverse'); + + return new graphic.Group( + (orient === 'horizontal' && !inverse) + ? {scale: itemAlign === 'bottom' ? [1, 1] : [-1, 1], rotation: Math.PI / 2} + : (orient === 'horizontal' && inverse) + ? {scale: itemAlign === 'bottom' ? [-1, 1] : [1, 1], rotation: -Math.PI / 2} + : (orient === 'vertical' && !inverse) + ? {scale: itemAlign === 'left' ? [1, -1] : [-1, -1]} + : {scale: itemAlign === 'left' ? [1, 1] : [-1, 1]} + ); + }, + + /** + * @private + */ + _updateHandle: function (handleEnds, visualInRange) { + if (!this._useHandle) { + return; + } + + var shapes = this._shapes; + var visualMapModel = this.visualMapModel; + var handleThumbs = shapes.handleThumbs; + var handleLabels = shapes.handleLabels; + + each([0, 1], function (handleIndex) { + var handleThumb = handleThumbs[handleIndex]; + handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]); + handleThumb.position[1] = handleEnds[handleIndex]; + + // Update handle label position. + var textPoint = graphic.applyTransform( + shapes.handleLabelPoints[handleIndex], + graphic.getTransform(handleThumb, this.group) + ); + handleLabels[handleIndex].setStyle({ + x: textPoint[0], + y: textPoint[1], + text: visualMapModel.formatValueText(this._dataInterval[handleIndex]), + textVerticalAlign: 'middle', + textAlign: this._applyTransform( + this._orient === 'horizontal' + ? (handleIndex === 0 ? 'bottom' : 'top') + : 'left', + shapes.barGroup + ) + }); + }, this); + }, + + /** + * @private + * @param {number} cursorValue + * @param {number} textValue + * @param {string} [rangeSymbol] + * @param {number} [halfHoverLinkSize] + */ + _showIndicator: function (cursorValue, textValue, rangeSymbol, halfHoverLinkSize) { + var visualMapModel = this.visualMapModel; + var dataExtent = visualMapModel.getExtent(); + var itemSize = visualMapModel.itemSize; + var sizeExtent = [0, itemSize[1]]; + var pos = linearMap(cursorValue, dataExtent, sizeExtent, true); + + var shapes = this._shapes; + var indicator = shapes.indicator; + if (!indicator) { + return; + } + + indicator.position[1] = pos; + indicator.attr('invisible', false); + indicator.setShape('points', createIndicatorPoints( + !!rangeSymbol, halfHoverLinkSize, pos, itemSize[1] + )); + + var opts = {convertOpacityToAlpha: true}; + var color = this.getControllerVisual(cursorValue, 'color', opts); + indicator.setStyle('fill', color); + + // Update handle label position. + var textPoint = graphic.applyTransform( + shapes.indicatorLabelPoint, + graphic.getTransform(indicator, this.group) + ); + + var indicatorLabel = shapes.indicatorLabel; + indicatorLabel.attr('invisible', false); + var align = this._applyTransform('left', shapes.barGroup); + var orient = this._orient; + indicatorLabel.setStyle({ + text: (rangeSymbol ? rangeSymbol : '') + visualMapModel.formatValueText(textValue), + textVerticalAlign: orient === 'horizontal' ? align : 'middle', + textAlign: orient === 'horizontal' ? 'center' : align, + x: textPoint[0], + y: textPoint[1] + }); + }, + + /** + * @private + */ + _enableHoverLinkToSeries: function () { + var self = this; + this._shapes.barGroup + + .on('mousemove', function (e) { + self._hovering = true; + + if (!self._dragging) { + var itemSize = self.visualMapModel.itemSize; + var pos = self._applyTransform( + [e.offsetX, e.offsetY], self._shapes.barGroup, true, true + ); + // For hover link show when hover handle, which might be + // below or upper than sizeExtent. + pos[1] = mathMin(mathMax(0, pos[1]), itemSize[1]); + + self._doHoverLinkToSeries( + pos[1], + 0 <= pos[0] && pos[0] <= itemSize[0] + ); + } + }) + + .on('mouseout', function () { + // When mouse is out of handle, hoverLink still need + // to be displayed when realtime is set as false. + self._hovering = false; + !self._dragging && self._clearHoverLinkToSeries(); + }); + }, + + /** + * @private + */ + _enableHoverLinkFromSeries: function () { + var zr = this.api.getZr(); + + if (this.visualMapModel.option.hoverLink) { + zr.on('mouseover', this._hoverLinkFromSeriesMouseOver, this); + zr.on('mouseout', this._hideIndicator, this); + } + else { + this._clearHoverLinkFromSeries(); + } + }, + + /** + * @private + */ + _doHoverLinkToSeries: function (cursorPos, hoverOnBar) { + var visualMapModel = this.visualMapModel; + var itemSize = visualMapModel.itemSize; + + if (!visualMapModel.option.hoverLink) { + return; + } + + var sizeExtent = [0, itemSize[1]]; + var dataExtent = visualMapModel.getExtent(); + + // For hover link show when hover handle, which might be below or upper than sizeExtent. + cursorPos = mathMin(mathMax(sizeExtent[0], cursorPos), sizeExtent[1]); + + var halfHoverLinkSize = getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent); + var hoverRange = [cursorPos - halfHoverLinkSize, cursorPos + halfHoverLinkSize]; + var cursorValue = linearMap(cursorPos, sizeExtent, dataExtent, true); + var valueRange = [ + linearMap(hoverRange[0], sizeExtent, dataExtent, true), + linearMap(hoverRange[1], sizeExtent, dataExtent, true) + ]; + // Consider data range is out of visualMap range, see test/visualMap-continuous.html, + // where china and india has very large population. + hoverRange[0] < sizeExtent[0] && (valueRange[0] = -Infinity); + hoverRange[1] > sizeExtent[1] && (valueRange[1] = Infinity); + + // Do not show indicator when mouse is over handle, + // otherwise labels overlap, especially when dragging. + if (hoverOnBar) { + if (valueRange[0] === -Infinity) { + this._showIndicator(cursorValue, valueRange[1], '< ', halfHoverLinkSize); + } + else if (valueRange[1] === Infinity) { + this._showIndicator(cursorValue, valueRange[0], '> ', halfHoverLinkSize); + } + else { + this._showIndicator(cursorValue, cursorValue, '≈ ', halfHoverLinkSize); + } + } + + // When realtime is set as false, handles, which are in barGroup, + // also trigger hoverLink, which help user to realize where they + // focus on when dragging. (see test/heatmap-large.html) + // When realtime is set as true, highlight will not show when hover + // handle, because the label on handle, which displays a exact value + // but not range, might mislead users. + var oldBatch = this._hoverLinkDataIndices; + var newBatch = []; + if (hoverOnBar || useHoverLinkOnHandle(visualMapModel)) { + newBatch = this._hoverLinkDataIndices = visualMapModel.findTargetDataIndices(valueRange); + } + + var resultBatches = modelUtil.compressBatches(oldBatch, newBatch); + this._dispatchHighDown('downplay', resultBatches[0]); + this._dispatchHighDown('highlight', resultBatches[1]); + }, + + /** + * @private + */ + _hoverLinkFromSeriesMouseOver: function (e) { + var el = e.target; + + if (!el || el.dataIndex == null) { + return; + } + + var dataModel = el.dataModel || this.ecModel.getSeriesByIndex(el.seriesIndex); + var data = dataModel.getData(el.dataType); + var dim = data.getDimension(this.visualMapModel.getDataDimension(data)); + var value = data.get(dim, el.dataIndex, true); + + if (!isNaN(value)) { + this._showIndicator(value, value); + } + }, + + /** + * @private + */ + _hideIndicator: function () { + var shapes = this._shapes; + shapes.indicator && shapes.indicator.attr('invisible', true); + shapes.indicatorLabel && shapes.indicatorLabel.attr('invisible', true); + }, + + /** + * @private + */ + _clearHoverLinkToSeries: function () { + this._hideIndicator(); + + var indices = this._hoverLinkDataIndices; + + this._dispatchHighDown('downplay', indices); + + indices.length = 0; + }, + + /** + * @private + */ + _clearHoverLinkFromSeries: function () { + this._hideIndicator(); + + var zr = this.api.getZr(); + zr.off('mouseover', this._hoverLinkFromSeriesMouseOver); + zr.off('mouseout', this._hideIndicator); + }, + + /** + * @private + */ + _applyTransform: function (vertex, element, inverse, global) { + var transform = graphic.getTransform(element, global ? null : this.group); + + return graphic[ + zrUtil.isArray(vertex) ? 'applyTransform' : 'transformDirection' + ](vertex, transform, inverse); + }, + + /** + * @private + */ + _dispatchHighDown: function (type, batch) { + batch && batch.length && this.api.dispatchAction({ + type: type, + batch: batch + }); + }, + + /** + * @override + */ + dispose: function () { + this._clearHoverLinkFromSeries(); + this._clearHoverLinkToSeries(); + }, + + /** + * @override + */ + remove: function () { + this._clearHoverLinkFromSeries(); + this._clearHoverLinkToSeries(); + } + + }); + + function createPolygon(points, cursor, onDrift, onDragEnd) { + return new graphic.Polygon({ + shape: {points: points}, + draggable: !!onDrift, + cursor: cursor, + drift: onDrift, + ondragend: onDragEnd + }); + } + + function createHandlePoints(handleIndex, textSize) { + return handleIndex === 0 + ? [[0, 0], [textSize, 0], [textSize, -textSize]] + : [[0, 0], [textSize, 0], [textSize, textSize]]; + } + + function createIndicatorPoints(isRange, halfHoverLinkSize, pos, extentMax) { + return isRange + ? [ // indicate range + [0, -mathMin(halfHoverLinkSize, mathMax(pos, 0))], + [HOVER_LINK_OUT, 0], + [0, mathMin(halfHoverLinkSize, mathMax(extentMax - pos, 0))] + ] + : [ // indicate single value + [0, 0], [5, -5], [5, 5] + ]; + } + + function getHalfHoverLinkSize(visualMapModel, dataExtent, sizeExtent) { + var halfHoverLinkSize = HOVER_LINK_SIZE / 2; + var hoverLinkDataSize = visualMapModel.get('hoverLinkDataSize'); + if (hoverLinkDataSize) { + halfHoverLinkSize = linearMap(hoverLinkDataSize, dataExtent, sizeExtent, true) / 2; + } + return halfHoverLinkSize; + } + + function useHoverLinkOnHandle(visualMapModel) { + return !visualMapModel.get('realtime') && visualMapModel.get('hoverLinkOnHandle'); + } + + module.exports = ContinuousView; + + + +/***/ }, +/* 339 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var formatUtil = __webpack_require__(6); + var layout = __webpack_require__(21); + var echarts = __webpack_require__(1); + var VisualMapping = __webpack_require__(192); + + module.exports = echarts.extendComponentView({ + + type: 'visualMap', + + /** + * @readOnly + * @type {Object} + */ + autoPositionValues: {left: 1, right: 1, top: 1, bottom: 1}, + + init: function (ecModel, api) { + /** + * @readOnly + * @type {module:echarts/model/Global} + */ + this.ecModel = ecModel; + + /** + * @readOnly + * @type {module:echarts/ExtensionAPI} + */ + this.api = api; + + /** + * @readOnly + * @type {module:echarts/component/visualMap/visualMapModel} + */ + this.visualMapModel; + }, + + /** + * @protected + */ + render: function (visualMapModel, ecModel, api, payload) { + this.visualMapModel = visualMapModel; + + if (visualMapModel.get('show') === false) { + this.group.removeAll(); + return; + } + + this.doRender.apply(this, arguments); + }, + + /** + * @protected + */ + renderBackground: function (group) { + var visualMapModel = this.visualMapModel; + var padding = formatUtil.normalizeCssArray(visualMapModel.get('padding') || 0); + var rect = group.getBoundingRect(); + + group.add(new graphic.Rect({ + z2: -1, // Lay background rect on the lowest layer. + silent: true, + shape: { + x: rect.x - padding[3], + y: rect.y - padding[0], + width: rect.width + padding[3] + padding[1], + height: rect.height + padding[0] + padding[2] + }, + style: { + fill: visualMapModel.get('backgroundColor'), + stroke: visualMapModel.get('borderColor'), + lineWidth: visualMapModel.get('borderWidth') + } + })); + }, + + /** + * @protected + * @param {number} targetValue + * @param {string=} visualCluster Only can be 'color' 'opacity' 'symbol' 'symbolSize' + * @param {Object} [opts] + * @param {string=} [opts.forceState] Specify state, instead of using getValueState method. + * @param {string=} [opts.convertOpacityToAlpha=false] For color gradient in controller widget. + * @return {*} Visual value. + */ + getControllerVisual: function (targetValue, visualCluster, opts) { + opts = opts || {}; + + var forceState = opts.forceState; + var visualMapModel = this.visualMapModel; + var visualObj = {}; + + // Default values. + if (visualCluster === 'symbol') { + visualObj.symbol = visualMapModel.get('itemSymbol'); + } + if (visualCluster === 'color') { + var defaultColor = visualMapModel.get('contentColor'); + visualObj.color = defaultColor; + } + + function getter(key) { + return visualObj[key]; + } + + function setter(key, value) { + visualObj[key] = value; + } + + var mappings = visualMapModel.controllerVisuals[ + forceState || visualMapModel.getValueState(targetValue) + ]; + var visualTypes = VisualMapping.prepareVisualTypes(mappings); + + zrUtil.each(visualTypes, function (type) { + var visualMapping = mappings[type]; + if (opts.convertOpacityToAlpha && type === 'opacity') { + type = 'colorAlpha'; + visualMapping = mappings.__alphaForOpacity; + } + if (VisualMapping.dependsOn(type, visualCluster)) { + visualMapping && visualMapping.applyVisual( + targetValue, getter, setter + ); + } + }); + + return visualObj[visualCluster]; + }, + + /** + * @protected + */ + positionGroup: function (group) { + var model = this.visualMapModel; + var api = this.api; + + layout.positionGroup( + group, + model.getBoxLayoutParams(), + {width: api.getWidth(), height: api.getHeight()} + ); + }, + + /** + * @protected + * @abstract + */ + doRender: zrUtil.noop + + }); + + + +/***/ }, +/* 340 */ +/***/ function(module, exports, __webpack_require__) { + + + + var layout = __webpack_require__(21); + + var helper = { + + /** + * @param {module:echarts/component/visualMap/VisualMapModel} visualMapModel\ + * @param {module:echarts/ExtensionAPI} api + * @param {Array.} itemSize always [short, long] + * @return {string} 'left' or 'right' or 'top' or 'bottom' + */ + getItemAlign: function (visualMapModel, api, itemSize) { + var modelOption = visualMapModel.option; + var itemAlign = modelOption.align; + + if (itemAlign != null && itemAlign !== 'auto') { + return itemAlign; + } + + // Auto decision align. + var ecSize = {width: api.getWidth(), height: api.getHeight()}; + var realIndex = modelOption.orient === 'horizontal' ? 1 : 0; + + var paramsSet = [ + ['left', 'right', 'width'], + ['top', 'bottom', 'height'] + ]; + var reals = paramsSet[realIndex]; + var fakeValue = [0, null, 10]; + + var layoutInput = {}; + for (var i = 0; i < 3; i++) { + layoutInput[paramsSet[1 - realIndex][i]] = fakeValue[i]; + layoutInput[reals[i]] = i === 2 ? itemSize[0] : modelOption[reals[i]]; + } + + var rParam = [['x', 'width', 3], ['y', 'height', 0]][realIndex]; + var rect = layout.getLayoutRect(layoutInput, ecSize, modelOption.padding); + + return reals[ + (rect.margin[rParam[2]] || 0) + rect[rParam[0]] + rect[rParam[1]] * 0.5 + < ecSize[rParam[1]] * 0.5 ? 0 : 1 + ]; + } + + }; + + + module.exports = helper; + + + +/***/ }, +/* 341 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data range action + */ + + + var echarts = __webpack_require__(1); + + var actionInfo = { + type: 'selectDataRange', + event: 'dataRangeSelected', + // FIXME use updateView appears wrong + update: 'update' + }; + + echarts.registerAction(actionInfo, function (payload, ecModel) { + + ecModel.eachComponent({mainType: 'visualMap', query: payload}, function (model) { + model.setSelected(payload.selected); + }); + + }); + + + +/***/ }, +/* 342 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(1).registerPreprocessor( + __webpack_require__(332) + ); + + __webpack_require__(333); + __webpack_require__(334); + __webpack_require__(343); + __webpack_require__(344); + __webpack_require__(341); + + + +/***/ }, +/* 343 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapModel = __webpack_require__(336); + var zrUtil = __webpack_require__(4); + var VisualMapping = __webpack_require__(192); + + var PiecewiseModel = VisualMapModel.extend({ + + type: 'visualMap.piecewise', + + /** + * Order Rule: + * + * option.categories / option.pieces / option.text / option.selected: + * If !option.inverse, + * Order when vertical: ['top', ..., 'bottom']. + * Order when horizontal: ['left', ..., 'right']. + * If option.inverse, the meaning of + * the order should be reversed. + * + * this._pieceList: + * The order is always [low, ..., high]. + * + * Mapping from location to low-high: + * If !option.inverse + * When vertical, top is high. + * When horizontal, right is high. + * If option.inverse, reverse. + */ + + /** + * @protected + */ + defaultOption: { + selected: null, // Object. If not specified, means selected. + // When pieces and splitNumber: {'0': true, '5': true} + // When categories: {'cate1': false, 'cate3': true} + // When selected === false, means all unselected. + align: 'auto', // 'auto', 'left', 'right' + itemWidth: 20, // When put the controller vertically, it is the length of + // horizontal side of each item. Otherwise, vertical side. + itemHeight: 14, // When put the controller vertically, it is the length of + // vertical side of each item. Otherwise, horizontal side. + itemSymbol: 'roundRect', + pieceList: null, // Each item is Object, with some of those attrs: + // {min, max, lt, gt, lte, gte, value, + // color, colorSaturation, colorAlpha, opacity, + // symbol, symbolSize}, which customize the range or visual + // coding of the certain piece. Besides, see "Order Rule". + categories: null, // category names, like: ['some1', 'some2', 'some3']. + // Attr min/max are ignored when categories set. See "Order Rule" + splitNumber: 5, // If set to 5, auto split five pieces equally. + // If set to 0 and component type not set, component type will be + // determined as "continuous". (It is less reasonable but for ec2 + // compatibility, see echarts/component/visualMap/typeDefaulter) + selectedMode: 'multiple', // Can be 'multiple' or 'single'. + itemGap: 10, // The gap between two items, in px. + hoverLink: true // Enable hover highlight. + }, + + /** + * @override + */ + optionUpdated: function (newOption, isInit) { + PiecewiseModel.superApply(this, 'optionUpdated', arguments); + + /** + * The order is always [low, ..., high]. + * [{text: string, interval: Array.}, ...] + * @private + * @type {Array.} + */ + this._pieceList = []; + + this.resetTargetSeries(); + this.resetExtent(); + + /** + * 'pieces', 'categories', 'splitNumber' + * @type {string} + */ + var mode = this._mode = this._determineMode(); + + resetMethods[this._mode].call(this); + + this._resetSelected(newOption, isInit); + + var categories = this.option.categories; + + this.resetVisual(function (mappingOption, state) { + if (mode === 'categories') { + mappingOption.mappingMethod = 'category'; + mappingOption.categories = zrUtil.clone(categories); + } + else { + mappingOption.dataExtent = this.getExtent(); + mappingOption.mappingMethod = 'piecewise'; + mappingOption.pieceList = zrUtil.map(this._pieceList, function (piece) { + var piece = zrUtil.clone(piece); + if (state !== 'inRange') { + piece.visual = null; + } + return piece; + }); + } + }); + }, + + _resetSelected: function (newOption, isInit) { + var thisOption = this.option; + var pieceList = this._pieceList; + + // Selected do not merge but all override. + var selected = (isInit ? thisOption : newOption).selected || {}; + thisOption.selected = selected; + + // Consider 'not specified' means true. + zrUtil.each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (!(key in selected)) { + selected[key] = true; + } + }, this); + + if (thisOption.selectedMode === 'single') { + // Ensure there is only one selected. + var hasSel = false; + + zrUtil.each(pieceList, function (piece, index) { + var key = this.getSelectedMapKey(piece); + if (selected[key]) { + hasSel + ? (selected[key] = false) + : (hasSel = true); + } + }, this); + } + // thisOption.selectedMode === 'multiple', default: all selected. + }, + + /** + * @public + */ + getSelectedMapKey: function (piece) { + return this._mode === 'categories' + ? piece.value + '' : piece.index + ''; + }, + + /** + * @public + */ + getPieceList: function () { + return this._pieceList; + }, + + /** + * @private + * @return {string} + */ + _determineMode: function () { + var option = this.option; + + return option.pieces && option.pieces.length > 0 + ? 'pieces' + : this.option.categories + ? 'categories' + : 'splitNumber'; + }, + + /** + * @public + * @override + */ + setSelected: function (selected) { + this.option.selected = zrUtil.clone(selected); + }, + + /** + * @public + * @override + */ + getValueState: function (value) { + var index = VisualMapping.findPieceIndex(value, this._pieceList); + + return index != null + ? (this.option.selected[this.getSelectedMapKey(this._pieceList[index])] + ? 'inRange' : 'outOfRange' + ) + : 'outOfRange'; + }, + + /** + * @public + * @params {number} pieceIndex piece index in visualMapModel.getPieceList() + * @return {Array.} [{seriesId, dataIndices: >}, ...] + */ + findTargetDataIndices: function (pieceIndex) { + var result = []; + + this.eachTargetSeries(function (seriesModel) { + var dataIndices = []; + var data = seriesModel.getData(); + + data.each(this.getDataDimension(data), function (value, dataIndex) { + // Should always base on model pieceList, because it is order sensitive. + var pIdx = VisualMapping.findPieceIndex(value, this._pieceList); + pIdx === pieceIndex && dataIndices.push(dataIndex); + }, true, this); + + result.push({seriesId: seriesModel.id, dataIndex: dataIndices}); + }, this); + + return result; + }, + + /** + * @private + */ + getRepresentValue: function (piece) { + var representValue; + if (this.isCategory()) { + representValue = piece.value; + } + else { + if (piece.value != null) { + representValue = piece.value; + } + else { + var pieceInterval = piece.interval || []; + representValue = (pieceInterval[0] + pieceInterval[1]) / 2; + } + } + return representValue; + }, + + getStops: function (seriesModel, getColorVisual) { + var result = []; + var model = this; + + var curr = -Infinity; + zrUtil.each(this._pieceList, function (piece) { + // Do not support category yet. + var interval = piece.interval; + if (interval) { + interval[0] > curr && setPiece({ + interval: [curr, interval[0]], + valueState: 'outOfRange' + }); + setPiece({ + interval: interval.slice(), + valueState: this.getValueState((interval[0] + interval[1]) / 2) + }); + curr = interval[1]; + } + }, this); + + return result; + + function setPiece(piece) { + result.push(piece); + piece.color = getColorVisual( + model, model.getRepresentValue(piece), piece.valueState + ); + } + } + + }); + + /** + * Key is this._mode + * @type {Object} + * @this {module:echarts/component/viusalMap/PiecewiseMode} + */ + var resetMethods = { + + splitNumber: function () { + var thisOption = this.option; + var pieceList = this._pieceList; + var precision = thisOption.precision; + var dataExtent = this.getExtent(); + var splitNumber = thisOption.splitNumber; + splitNumber = Math.max(parseInt(splitNumber, 10), 1); + thisOption.splitNumber = splitNumber; + + var splitStep = (dataExtent[1] - dataExtent[0]) / splitNumber; + // Precision auto-adaption + while (+splitStep.toFixed(precision) !== splitStep && precision < 5) { + precision++; + } + thisOption.precision = precision; + splitStep = +splitStep.toFixed(precision); + + for (var i = 0, curr = dataExtent[0]; i < splitNumber; i++, curr += splitStep) { + var max = i === splitNumber - 1 ? dataExtent[1] : (curr + splitStep); + + pieceList.push({ + index: i, + interval: [curr, max], + close: [1, 1] + }); + } + + normalizePieces(pieceList); + + zrUtil.each(pieceList, function (piece) { + piece.text = this.formatValueText(piece.interval); + }, this); + }, + + categories: function () { + var thisOption = this.option; + zrUtil.each(thisOption.categories, function (cate) { + // FIXME category模式也使用pieceList,但在visualMapping中不是使用pieceList。 + // 是否改一致。 + this._pieceList.push({ + text: this.formatValueText(cate, true), + value: cate + }); + }, this); + + // See "Order Rule". + normalizeReverse(thisOption, this._pieceList); + }, + + pieces: function () { + var thisOption = this.option; + var pieceList = this._pieceList; + + zrUtil.each(thisOption.pieces, function (pieceListItem, index) { + + if (!zrUtil.isObject(pieceListItem)) { + pieceListItem = {value: pieceListItem}; + } + + var item = {text: '', index: index}; + + if (pieceListItem.label != null) { + item.text = pieceListItem.label; + } + + if (pieceListItem.hasOwnProperty('value')) { + var value = item.value = pieceListItem.value; + item.interval = [value, value]; + item.close = [1, 1]; + } + else { + // `min` `max` is legacy option. + // `lt` `gt` `lte` `gte` is recommanded. + var interval = item.interval = []; + var close = item.close = [0, 0]; + + var closeList = [1, 0, 1]; + var infinityList = [-Infinity, Infinity]; + + var useMinMax = []; + for (var lg = 0; lg < 2; lg++) { + var names = [['gte', 'gt', 'min'], ['lte', 'lt', 'max']][lg]; + for (var i = 0; i < 3 && interval[lg] == null; i++) { + interval[lg] = pieceListItem[names[i]]; + close[lg] = closeList[i]; + useMinMax[lg] = i === 2; + } + interval[lg] == null && (interval[lg] = infinityList[lg]); + } + useMinMax[0] && interval[1] === Infinity && (close[0] = 0); + useMinMax[1] && interval[0] === -Infinity && (close[1] = 0); + + if (true) { + if (interval[0] > interval[1]) { + console.warn( + 'Piece ' + index + 'is illegal: ' + interval + + ' lower bound should not greater then uppper bound.' + ); + } + } + + if (interval[0] === interval[1] && close[0] && close[1]) { + // Consider: [{min: 5, max: 5, visual: {...}}, {min: 0, max: 5}], + // we use value to lift the priority when min === max + item.value = interval[0]; + } + } + + item.visual = VisualMapping.retrieveVisuals(pieceListItem); + + pieceList.push(item); + + }, this); + + // See "Order Rule". + normalizeReverse(thisOption, pieceList); + // Only pieces + normalizePieces(pieceList); + + zrUtil.each(pieceList, function (piece) { + var close = piece.close; + var edgeSymbols = [['<', '≤'][close[1]], ['>', '≥'][close[0]]]; + piece.text = piece.text || this.formatValueText( + piece.value != null ? piece.value : piece.interval, + false, + edgeSymbols + ); + }, this); + } + }; + + function normalizeReverse(thisOption, pieceList) { + var inverse = thisOption.inverse; + if (thisOption.orient === 'vertical' ? !inverse : inverse) { + pieceList.reverse(); + } + } + + // Reorder, remove duplicate, which are needed when using gradient. + // Not applicable for categories. + function normalizePieces(pieceList) { + pieceList.sort(function (a, b) { + return littleThan(a, b) ? -1 : 1; + }); + + var curr = -Infinity; + for (var i = 0; i < pieceList.length; i++) { + var interval = pieceList[i].interval; + var close = pieceList[i].close; + for (var lg = 0; lg < 2; lg++) { + if (interval[lg] < curr) { + interval[lg] = curr; + close[lg] = 1 - lg; + } + curr = interval[lg]; + } + } + // console.log(JSON.stringify(pieceList.map(a => a.interval))); + + function littleThan(piece, standard, lg) { + lg = lg || 0; + return piece.interval[lg] < standard.interval[lg] + || ( + piece.interval[lg] === standard.interval[lg] + && ( + +piece.close[lg] > standard.close[lg] + || littleThan(piece, standard, 1) + ) + ); + } + } + + module.exports = PiecewiseModel; + + +/***/ }, +/* 344 */ +/***/ function(module, exports, __webpack_require__) { + + + + var VisualMapView = __webpack_require__(339); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var symbolCreators = __webpack_require__(106); + var layout = __webpack_require__(21); + var helper = __webpack_require__(340); + + var PiecewiseVisualMapView = VisualMapView.extend({ + + type: 'visualMap.piecewise', + + /** + * @protected + * @override + */ + doRender: function () { + var thisGroup = this.group; + + thisGroup.removeAll(); + + var visualMapModel = this.visualMapModel; + var textGap = visualMapModel.get('textGap'); + var textStyleModel = visualMapModel.textStyleModel; + var textFont = textStyleModel.getFont(); + var textFill = textStyleModel.getTextColor(); + var itemAlign = this._getItemAlign(); + var itemSize = visualMapModel.itemSize; + + var viewData = this._getViewData(); + var showLabel = !viewData.endsText; + var showEndsText = !showLabel; + + showEndsText && this._renderEndsText(thisGroup, viewData.endsText[0], itemSize); + + zrUtil.each(viewData.viewPieceList, renderItem, this); + + showEndsText && this._renderEndsText(thisGroup, viewData.endsText[1], itemSize); + + layout.box( + visualMapModel.get('orient'), thisGroup, visualMapModel.get('itemGap') + ); + + this.renderBackground(thisGroup); + + this.positionGroup(thisGroup); + + function renderItem(item) { + var piece = item.piece; + + var itemGroup = new graphic.Group(); + itemGroup.onclick = zrUtil.bind(this._onItemClick, this, piece); + + this._enableHoverLink(itemGroup, item.indexInModelPieceList); + + var representValue = visualMapModel.getRepresentValue(piece); + + this._createItemSymbol( + itemGroup, representValue, [0, 0, itemSize[0], itemSize[1]] + ); + + if (showLabel) { + var visualState = this.visualMapModel.getValueState(representValue); + + itemGroup.add(new graphic.Text({ + style: { + x: itemAlign === 'right' ? -textGap : itemSize[0] + textGap, + y: itemSize[1] / 2, + text: piece.text, + textVerticalAlign: 'middle', + textAlign: itemAlign, + textFont: textFont, + fill: textFill, + opacity: visualState === 'outOfRange' ? 0.5 : 1 + } + })); + } + + thisGroup.add(itemGroup); + } + }, + + /** + * @private + */ + _enableHoverLink: function (itemGroup, pieceIndex) { + itemGroup + .on('mouseover', zrUtil.bind(onHoverLink, this, 'highlight')) + .on('mouseout', zrUtil.bind(onHoverLink, this, 'downplay')); + + function onHoverLink(method) { + var visualMapModel = this.visualMapModel; + + visualMapModel.option.hoverLink && this.api.dispatchAction({ + type: method, + batch: visualMapModel.findTargetDataIndices(pieceIndex) + }); + } + }, + + /** + * @private + */ + _getItemAlign: function () { + var visualMapModel = this.visualMapModel; + var modelOption = visualMapModel.option; + + if (modelOption.orient === 'vertical') { + return helper.getItemAlign( + visualMapModel, this.api, visualMapModel.itemSize + ); + } + else { // horizontal, most case left unless specifying right. + var align = modelOption.align; + if (!align || align === 'auto') { + align = 'left'; + } + return align; + } + }, + + /** + * @private + */ + _renderEndsText: function (group, text, itemSize) { + if (!text) { + return; + } + + var itemGroup = new graphic.Group(); + var textStyleModel = this.visualMapModel.textStyleModel; + + itemGroup.add(new graphic.Text({ + style: { + x: itemSize[0] / 2, + y: itemSize[1] / 2, + textVerticalAlign: 'middle', + textAlign: 'center', + text: text, + textFont: textStyleModel.getFont(), + fill: textStyleModel.getTextColor() + } + })); + + group.add(itemGroup); + }, + + /** + * @private + * @return {Object} {peiceList, endsText} The order is the same as screen pixel order. + */ + _getViewData: function () { + var visualMapModel = this.visualMapModel; + + var viewPieceList = zrUtil.map(visualMapModel.getPieceList(), function (piece, index) { + return {piece: piece, indexInModelPieceList: index}; + }); + var endsText = visualMapModel.get('text'); + + // Consider orient and inverse. + var orient = visualMapModel.get('orient'); + var inverse = visualMapModel.get('inverse'); + + // Order of model pieceList is always [low, ..., high] + if (orient === 'horizontal' ? inverse : !inverse) { + viewPieceList.reverse(); + } + // Origin order of endsText is [high, low] + else if (endsText) { + endsText = endsText.slice().reverse(); + } + + return {viewPieceList: viewPieceList, endsText: endsText}; + }, + + /** + * @private + */ + _createItemSymbol: function (group, representValue, shapeParam) { + group.add(symbolCreators.createSymbol( + this.getControllerVisual(representValue, 'symbol'), + shapeParam[0], shapeParam[1], shapeParam[2], shapeParam[3], + this.getControllerVisual(representValue, 'color') + )); + }, + + /** + * @private + */ + _onItemClick: function (piece) { + var visualMapModel = this.visualMapModel; + var option = visualMapModel.option; + var selected = zrUtil.clone(option.selected); + var newKey = visualMapModel.getSelectedMapKey(piece); + + if (option.selectedMode === 'single') { + selected[newKey] = true; + zrUtil.each(selected, function (o, key) { + selected[key] = key === newKey; + }); + } + else { + selected[newKey] = !selected[newKey]; + } + + this.api.dispatchAction({ + type: 'selectDataRange', + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: selected + }); + } + }); + + module.exports = PiecewiseVisualMapView; + + + +/***/ }, +/* 345 */ +/***/ function(module, exports, __webpack_require__) { + + // HINT Markpoint can't be used too much + + + __webpack_require__(346); + __webpack_require__(348); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markPoint component is enabled + opt.markPoint = opt.markPoint || {}; + }); + + +/***/ }, +/* 346 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markPoint', + + defaultOption: { + zlevel: 0, + z: 5, + symbol: 'pin', + symbolSize: 50, + //symbolRotate: 0, + //symbolOffset: [0, 0] + tooltip: { + trigger: 'item' + }, + label: { + normal: { + show: true, + position: 'inside' + }, + emphasis: { + show: true + } + }, + itemStyle: { + normal: { + borderWidth: 2 + } + } + } + }); + + +/***/ }, +/* 347 */ +/***/ function(module, exports, __webpack_require__) { + + + + var modelUtil = __webpack_require__(5); + var zrUtil = __webpack_require__(4); + var env = __webpack_require__(2); + + var formatUtil = __webpack_require__(6); + var addCommas = formatUtil.addCommas; + var encodeHTML = formatUtil.encodeHTML; + + function fillLabel(opt) { + modelUtil.defaultEmphasis( + opt.label, + modelUtil.LABEL_OPTIONS + ); + } + var MarkerModel = __webpack_require__(1).extendComponentModel({ + + type: 'marker', + + dependencies: ['series', 'grid', 'polar', 'geo'], + /** + * @overrite + */ + init: function (option, parentModel, ecModel, extraOpt) { + + if (true) { + if (this.type === 'marker') { + throw new Error('Marker component is abstract component. Use markLine, markPoint, markArea instead.'); + } + } + this.mergeDefaultAndTheme(option, ecModel); + this.mergeOption(option, ecModel, extraOpt.createdBySelf, true); + }, + + /** + * @return {boolean} + */ + ifEnableAnimation: function () { + if (env.node) { + return false; + } + + var hostSeries = this.__hostSeries; + return this.getShallow('animation') && hostSeries && hostSeries.ifEnableAnimation(); + }, + + mergeOption: function (newOpt, ecModel, createdBySelf, isInit) { + var MarkerModel = this.constructor; + var modelPropName = this.mainType + 'Model'; + if (!createdBySelf) { + ecModel.eachSeries(function (seriesModel) { + + var markerOpt = seriesModel.get(this.mainType); + + var markerModel = seriesModel[modelPropName]; + if (!markerOpt || !markerOpt.data) { + seriesModel[modelPropName] = null; + return; + } + if (!markerModel) { + if (isInit) { + // Default label emphasis `position` and `show` + fillLabel(markerOpt); + } + zrUtil.each(markerOpt.data, function (item) { + // FIXME Overwrite fillLabel method ? + if (item instanceof Array) { + fillLabel(item[0]); + fillLabel(item[1]); + } + else { + fillLabel(item); + } + }); + var opt = { + mainType: this.mainType, + // Use the same series index and name + seriesIndex: seriesModel.seriesIndex, + name: seriesModel.name, + createdBySelf: true + }; + markerModel = new MarkerModel( + markerOpt, this, ecModel, opt + ); + markerModel.__hostSeries = seriesModel; + } + else { + markerModel.mergeOption(markerOpt, ecModel, true); + } + seriesModel[modelPropName] = markerModel; + }, this); + } + }, + + formatTooltip: function (dataIndex) { + var data = this.getData(); + var value = this.getRawValue(dataIndex); + var formattedValue = zrUtil.isArray(value) + ? zrUtil.map(value, addCommas).join(', ') : addCommas(value); + var name = data.getName(dataIndex); + var html = this.name; + if (value != null || name) { + html += '
'; + } + if (name) { + html += encodeHTML(name); + if (value != null) { + html += ' : '; + } + } + if (value != null) { + html += formattedValue; + } + return html; + }, + + getData: function () { + return this._data; + }, + + setData: function (data) { + this._data = data; + } + }); + + zrUtil.mixin(MarkerModel, modelUtil.dataFormatMixin); + + module.exports = MarkerModel; + + +/***/ }, +/* 348 */ +/***/ function(module, exports, __webpack_require__) { + + + + var SymbolDraw = __webpack_require__(104); + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + + var List = __webpack_require__(97); + + var markerHelper = __webpack_require__(349); + + function updateMarkerLayout(mpData, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var point; + var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + // Chart like bar may have there own marker positioning logic + else if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + mpData.getValues(mpData.dimensions, idx) + ); + } + else if (coordSys) { + var x = mpData.get(coordSys.dimensions[0], idx); + var y = mpData.get(coordSys.dimensions[1], idx); + point = coordSys.dataToPoint([x, y]); + + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + + mpData.setItemLayout(idx, point); + }); + } + + __webpack_require__(350).extend({ + + type: 'markPoint', + + updateLayout: function (markPointModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mpModel = seriesModel.markPointModel; + if (mpModel) { + updateMarkerLayout(mpModel.getData(), seriesModel, api); + this.markerGroupMap[seriesModel.name].updateLayout(mpModel); + } + }, this); + }, + + renderSeries: function (seriesModel, mpModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var symbolDrawMap = this.markerGroupMap; + var symbolDraw = symbolDrawMap[seriesName]; + if (!symbolDraw) { + symbolDraw = symbolDrawMap[seriesName] = new SymbolDraw(); + } + + var mpData = createList(coordSys, seriesModel, mpModel); + + // FIXME + mpModel.setData(mpData); + + updateMarkerLayout(mpModel.getData(), seriesModel, api); + + mpData.each(function (idx) { + var itemModel = mpData.getItemModel(idx); + var symbolSize = itemModel.getShallow('symbolSize'); + if (typeof symbolSize === 'function') { + // FIXME 这里不兼容 ECharts 2.x,2.x 貌似参数是整个数据? + symbolSize = symbolSize( + mpModel.getRawValue(idx), mpModel.getDataParams(idx) + ); + } + mpData.setItemVisual(idx, { + symbolSize: symbolSize, + color: itemModel.get('itemStyle.normal.color') + || seriesData.getVisual('color'), + symbol: itemModel.getShallow('symbol') + }); + }); + + // TODO Text are wrong + symbolDraw.updateData(mpData); + this.group.add(symbolDraw.group); + + // Set host model for tooltip + // FIXME + mpData.eachItemGraphicEl(function (el) { + el.traverse(function (child) { + child.dataModel = mpModel; + }); + }); + + symbolDraw.__keep = true; + + symbolDraw.group.silent = mpModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} [coordSys] + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, mpModel) { + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + } + + var mpData = new List(coordDimsInfos, mpModel); + var dataOpt = zrUtil.map(mpModel.get('data'), zrUtil.curry( + markerHelper.dataTransform, seriesModel + )); + if (coordSys) { + dataOpt = zrUtil.filter( + dataOpt, zrUtil.curry(markerHelper.dataFilter, coordSys) + ); + } + + mpData.initData(dataOpt, null, + coordSys ? markerHelper.dimValueGetter : function (item) { + return item.value; + } + ); + return mpData; + } + + + +/***/ }, +/* 349 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var numberUtil = __webpack_require__(7); + var indexOf = zrUtil.indexOf; + + function hasXOrY(item) { + return !(isNaN(parseFloat(item.x)) && isNaN(parseFloat(item.y))); + } + + function hasXAndY(item) { + return !isNaN(parseFloat(item.x)) && !isNaN(parseFloat(item.y)); + } + + function getPrecision(data, valueAxisDim, dataIndex) { + var precision = -1; + do { + precision = Math.max( + numberUtil.getPrecision(data.get( + valueAxisDim, dataIndex + )), + precision + ); + data = data.stackedOn; + } while (data); + + return precision; + } + + function markerTypeCalculatorWithExtent( + mlType, data, otherDataDim, targetDataDim, otherCoordIndex, targetCoordIndex + ) { + var coordArr = []; + var value = numCalculate(data, targetDataDim, mlType); + + var dataIndex = data.indexOfNearest(targetDataDim, value, true); + coordArr[otherCoordIndex] = data.get(otherDataDim, dataIndex, true); + coordArr[targetCoordIndex] = data.get(targetDataDim, dataIndex, true); + + var precision = getPrecision(data, targetDataDim, dataIndex); + if (precision >= 0) { + coordArr[targetCoordIndex] = +coordArr[targetCoordIndex].toFixed(precision); + } + + return coordArr; + } + + var curry = zrUtil.curry; + // TODO Specified percent + var markerTypeCalculator = { + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + min: curry(markerTypeCalculatorWithExtent, 'min'), + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + max: curry(markerTypeCalculatorWithExtent, 'max'), + + /** + * @method + * @param {module:echarts/data/List} data + * @param {string} baseAxisDim + * @param {string} valueAxisDim + */ + average: curry(markerTypeCalculatorWithExtent, 'average') + }; + + /** + * Transform markPoint data item to format used in List by do the following + * 1. Calculate statistic like `max`, `min`, `average` + * 2. Convert `item.xAxis`, `item.yAxis` to `item.coord` array + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/coord/*} [coordSys] + * @param {Object} item + * @return {Object} + */ + var dataTransform = function (seriesModel, item) { + var data = seriesModel.getData(); + var coordSys = seriesModel.coordinateSystem; + + // 1. If not specify the position with pixel directly + // 2. If `coord` is not a data array. Which uses `xAxis`, + // `yAxis` to specify the coord on each dimension + + // parseFloat first because item.x and item.y can be percent string like '20%' + if (item && !hasXAndY(item) && !zrUtil.isArray(item.coord) && coordSys) { + var dims = coordSys.dimensions; + var axisInfo = getAxisInfo(item, data, coordSys, seriesModel); + + // Clone the option + // Transform the properties xAxis, yAxis, radiusAxis, angleAxis, geoCoord to value + item = zrUtil.clone(item); + + if (item.type + && markerTypeCalculator[item.type] + && axisInfo.baseAxis && axisInfo.valueAxis + ) { + var otherCoordIndex = indexOf(dims, axisInfo.baseAxis.dim); + var targetCoordIndex = indexOf(dims, axisInfo.valueAxis.dim); + + item.coord = markerTypeCalculator[item.type]( + data, axisInfo.baseDataDim, axisInfo.valueDataDim, + otherCoordIndex, targetCoordIndex + ); + // Force to use the value of calculated value. + item.value = item.coord[targetCoordIndex]; + } + else { + // FIXME Only has one of xAxis and yAxis. + var coord = [ + item.xAxis != null ? item.xAxis : item.radiusAxis, + item.yAxis != null ? item.yAxis : item.angleAxis + ]; + // Each coord support max, min, average + for (var i = 0; i < 2; i++) { + if (markerTypeCalculator[coord[i]]) { + var dataDim = seriesModel.coordDimToDataDim(dims[i])[0]; + coord[i] = numCalculate(data, dataDim, coord[i]); + } + } + item.coord = coord; + } + } + return item; + }; + + var getAxisInfo = function (item, data, coordSys, seriesModel) { + var ret = {}; + + if (item.valueIndex != null || item.valueDim != null) { + ret.valueDataDim = item.valueIndex != null + ? data.getDimension(item.valueIndex) : item.valueDim; + ret.valueAxis = coordSys.getAxis(seriesModel.dataDimToCoordDim(ret.valueDataDim)); + ret.baseAxis = coordSys.getOtherAxis(ret.valueAxis); + ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0]; + } + else { + ret.baseAxis = seriesModel.getBaseAxis(); + ret.valueAxis = coordSys.getOtherAxis(ret.baseAxis); + ret.baseDataDim = seriesModel.coordDimToDataDim(ret.baseAxis.dim)[0]; + ret.valueDataDim = seriesModel.coordDimToDataDim(ret.valueAxis.dim)[0]; + } + + return ret; + }; + + /** + * Filter data which is out of coordinateSystem range + * [dataFilter description] + * @param {module:echarts/coord/*} [coordSys] + * @param {Object} item + * @return {boolean} + */ + var dataFilter = function (coordSys, item) { + // Alwalys return true if there is no coordSys + return (coordSys && coordSys.containData && item.coord && !hasXOrY(item)) + ? coordSys.containData(item.coord) : true; + }; + + var dimValueGetter = function (item, dimName, dataIndex, dimIndex) { + // x, y, radius, angle + if (dimIndex < 2) { + return item.coord && item.coord[dimIndex]; + } + return item.value; + }; + + var numCalculate = function (data, valueDataDim, type) { + if (type === 'average') { + var sum = 0; + var count = 0; + data.each(valueDataDim, function (val, idx) { + if (!isNaN(val)) { + sum += val; + count++; + } + }, true); + return sum / count; + } + else { + return data.getDataExtent(valueDataDim, true)[type === 'max' ? 1 : 0]; + } + }; + + module.exports = { + dataTransform: dataTransform, + dataFilter: dataFilter, + dimValueGetter: dimValueGetter, + getAxisInfo: getAxisInfo, + numCalculate: numCalculate + }; + + +/***/ }, +/* 350 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'marker', + + init: function () { + /** + * Markline grouped by series + * @private + * @type {Object} + */ + this.markerGroupMap = {}; + }, + + render: function (markerModel, ecModel, api) { + var markerGroupMap = this.markerGroupMap; + for (var name in markerGroupMap) { + markerGroupMap[name].__keep = false; + } + + var markerModelKey = this.type + 'Model'; + ecModel.eachSeries(function (seriesModel) { + var markerModel = seriesModel[markerModelKey]; + markerModel && this.renderSeries(seriesModel, markerModel, ecModel, api); + }, this); + + for (var name in markerGroupMap) { + if (!markerGroupMap[name].__keep) { + this.group.remove(markerGroupMap[name].group); + } + } + }, + + renderSeries: function () {} + }); + + +/***/ }, +/* 351 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(352); + __webpack_require__(353); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markLine component is enabled + opt.markLine = opt.markLine || {}; + }); + + +/***/ }, +/* 352 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markLine', + + defaultOption: { + zlevel: 0, + z: 5, + + symbol: ['circle', 'arrow'], + symbolSize: [8, 16], + + //symbolRotate: 0, + + precision: 2, + tooltip: { + trigger: 'item' + }, + label: { + normal: { + show: true, + position: 'end' + }, + emphasis: { + show: true + } + }, + lineStyle: { + normal: { + type: 'dashed' + }, + emphasis: { + width: 3 + } + }, + animationEasing: 'linear' + } + }); + + +/***/ }, +/* 353 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var List = __webpack_require__(97); + var numberUtil = __webpack_require__(7); + + var markerHelper = __webpack_require__(349); + + var LineDraw = __webpack_require__(199); + + var markLineTransform = function (seriesModel, coordSys, mlModel, item) { + var data = seriesModel.getData(); + // Special type markLine like 'min', 'max', 'average' + var mlType = item.type; + + if (!zrUtil.isArray(item) + && ( + mlType === 'min' || mlType === 'max' || mlType === 'average' + // In case + // data: [{ + // yAxis: 10 + // }] + || (item.xAxis != null || item.yAxis != null) + ) + ) { + var valueAxis; + var valueDataDim; + var value; + + if (item.yAxis != null || item.xAxis != null) { + valueDataDim = item.yAxis != null ? 'y' : 'x'; + valueAxis = coordSys.getAxis(valueDataDim); + + value = zrUtil.retrieve(item.yAxis, item.xAxis); + } + else { + var axisInfo = markerHelper.getAxisInfo(item, data, coordSys, seriesModel); + valueDataDim = axisInfo.valueDataDim; + valueAxis = axisInfo.valueAxis; + value = markerHelper.numCalculate(data, valueDataDim, mlType); + } + var valueIndex = valueDataDim === 'x' ? 0 : 1; + var baseIndex = 1 - valueIndex; + + var mlFrom = zrUtil.clone(item); + var mlTo = {}; + + mlFrom.type = null; + + mlFrom.coord = []; + mlTo.coord = []; + mlFrom.coord[baseIndex] = -Infinity; + mlTo.coord[baseIndex] = Infinity; + + var precision = mlModel.get('precision'); + if (precision >= 0) { + value = +value.toFixed(precision); + } + + mlFrom.coord[valueIndex] = mlTo.coord[valueIndex] = value; + + item = [mlFrom, mlTo, { // Extra option for tooltip and label + type: mlType, + valueIndex: item.valueIndex, + // Force to use the value of calculated value. + value: value + }]; + } + + item = [ + markerHelper.dataTransform(seriesModel, item[0]), + markerHelper.dataTransform(seriesModel, item[1]), + zrUtil.extend({}, item[2]) + ]; + + // Avoid line data type is extended by from(to) data type + item[2].type = item[2].type || ''; + + // Merge from option and to option into line option + zrUtil.merge(item[2], item[0]); + zrUtil.merge(item[2], item[1]); + + return item; + }; + + function isInifinity(val) { + return !isNaN(val) && !isFinite(val); + } + + // If a markLine has one dim + function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + var dimName = coordSys.dimensions[dimIndex]; + return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]) + && fromCoord[dimIndex] === toCoord[dimIndex] && coordSys.getAxis(dimName).containData(fromCoord[dimIndex]); + } + + function markLineFilter(coordSys, item) { + if (coordSys.type === 'cartesian2d') { + var fromCoord = item[0].coord; + var toCoord = item[1].coord; + // In case + // { + // markLine: { + // data: [{ yAxis: 2 }] + // } + // } + if ( + fromCoord && toCoord && + (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) + || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys)) + ) { + return true; + } + } + return markerHelper.dataFilter(coordSys, item[0]) + && markerHelper.dataFilter(coordSys, item[1]); + } + + function updateSingleMarkerEndLayout( + data, idx, isFrom, seriesModel, api + ) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + + var point; + var xPx = numberUtil.parsePercent(itemModel.get('x'), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get('y'), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + data.getValues(data.dimensions, idx) + ); + } + else { + var dims = coordSys.dimensions; + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + point = coordSys.dataToPoint([x, y]); + } + // Expand line to the edge of grid if value on one axis is Inifnity + // In case + // markLine: { + // data: [{ + // yAxis: 2 + // // or + // type: 'average' + // }] + // } + if (coordSys.type === 'cartesian2d') { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var dims = coordSys.dimensions; + if (isInifinity(data.get(dims[0], idx))) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[isFrom ? 0 : 1]); + } + else if (isInifinity(data.get(dims[1], idx))) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[isFrom ? 0 : 1]); + } + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + + data.setItemLayout(idx, point); + } + + __webpack_require__(350).extend({ + + type: 'markLine', + + updateLayout: function (markLineModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var mlModel = seriesModel.markLineModel; + if (mlModel) { + var mlData = mlModel.getData(); + var fromData = mlModel.__from; + var toData = mlModel.__to; + // Update visual and layout of from symbol and to symbol + fromData.each(function (idx) { + updateSingleMarkerEndLayout(fromData, idx, true, seriesModel, api); + updateSingleMarkerEndLayout(toData, idx, false, seriesModel, api); + }); + // Update layout of line + mlData.each(function (idx) { + mlData.setItemLayout(idx, [ + fromData.getItemLayout(idx), + toData.getItemLayout(idx) + ]); + }); + + this.markerGroupMap[seriesModel.name].updateLayout(); + + } + }, this); + }, + + renderSeries: function (seriesModel, mlModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var lineDrawMap = this.markerGroupMap; + var lineDraw = lineDrawMap[seriesName]; + if (!lineDraw) { + lineDraw = lineDrawMap[seriesName] = new LineDraw(); + } + this.group.add(lineDraw.group); + + var mlData = createList(coordSys, seriesModel, mlModel); + + var fromData = mlData.from; + var toData = mlData.to; + var lineData = mlData.line; + + mlModel.__from = fromData; + mlModel.__to = toData; + // Line data for tooltip and formatter + mlModel.setData(lineData); + + var symbolType = mlModel.get('symbol'); + var symbolSize = mlModel.get('symbolSize'); + if (!zrUtil.isArray(symbolType)) { + symbolType = [symbolType, symbolType]; + } + if (typeof symbolSize === 'number') { + symbolSize = [symbolSize, symbolSize]; + } + + // Update visual and layout of from symbol and to symbol + mlData.from.each(function (idx) { + updateDataVisualAndLayout(fromData, idx, true); + updateDataVisualAndLayout(toData, idx, false); + }); + + // Update visual and layout of line + lineData.each(function (idx) { + var lineColor = lineData.getItemModel(idx).get('lineStyle.normal.color'); + lineData.setItemVisual(idx, { + color: lineColor || fromData.getItemVisual(idx, 'color') + }); + lineData.setItemLayout(idx, [ + fromData.getItemLayout(idx), + toData.getItemLayout(idx) + ]); + + lineData.setItemVisual(idx, { + 'fromSymbolSize': fromData.getItemVisual(idx, 'symbolSize'), + 'fromSymbol': fromData.getItemVisual(idx, 'symbol'), + 'toSymbolSize': toData.getItemVisual(idx, 'symbolSize'), + 'toSymbol': toData.getItemVisual(idx, 'symbol') + }); + }); + + lineDraw.updateData(lineData); + + // Set host model for tooltip + // FIXME + mlData.line.eachItemGraphicEl(function (el, idx) { + el.traverse(function (child) { + child.dataModel = mlModel; + }); + }); + + function updateDataVisualAndLayout(data, idx, isFrom) { + var itemModel = data.getItemModel(idx); + + updateSingleMarkerEndLayout( + data, idx, isFrom, seriesModel, api + ); + + data.setItemVisual(idx, { + symbolSize: itemModel.get('symbolSize') || symbolSize[isFrom ? 0 : 1], + symbol: itemModel.get('symbol', true) || symbolType[isFrom ? 0 : 1], + color: itemModel.get('itemStyle.normal.color') || seriesData.getVisual('color') + }); + } + + lineDraw.__keep = true; + + lineDraw.group.silent = mlModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} coordSys + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, mlModel) { + + var coordDimsInfos; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + } + + var fromData = new List(coordDimsInfos, mlModel); + var toData = new List(coordDimsInfos, mlModel); + // No dimensions + var lineData = new List([], mlModel); + + var optData = zrUtil.map(mlModel.get('data'), zrUtil.curry( + markLineTransform, seriesModel, coordSys, mlModel + )); + if (coordSys) { + optData = zrUtil.filter( + optData, zrUtil.curry(markLineFilter, coordSys) + ); + } + var dimValueGetter = coordSys ? markerHelper.dimValueGetter : function (item) { + return item.value; + }; + fromData.initData( + zrUtil.map(optData, function (item) { return item[0]; }), + null, dimValueGetter + ); + toData.initData( + zrUtil.map(optData, function (item) { return item[1]; }), + null, dimValueGetter + ); + lineData.initData( + zrUtil.map(optData, function (item) { return item[2]; }) + ); + lineData.hasItemOption = true; + return { + from: fromData, + to: toData, + line: lineData + }; + } + + +/***/ }, +/* 354 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(355); + __webpack_require__(356); + + __webpack_require__(1).registerPreprocessor(function (opt) { + // Make sure markArea component is enabled + opt.markArea = opt.markArea || {}; + }); + + +/***/ }, +/* 355 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(347).extend({ + + type: 'markArea', + + defaultOption: { + zlevel: 0, + // PENDING + z: 1, + tooltip: { + trigger: 'item' + }, + // markArea should fixed on the coordinate system + animation: false, + label: { + normal: { + show: true, + position: 'top' + }, + emphasis: { + show: true, + position: 'top' + } + }, + itemStyle: { + normal: { + // color and borderColor default to use color from series + // color: 'auto' + // borderColor: 'auto' + borderWidth: 0 + } + } + } + }); + + +/***/ }, +/* 356 */ +/***/ function(module, exports, __webpack_require__) { + + // TODO Better on polar + + + var zrUtil = __webpack_require__(4); + var List = __webpack_require__(97); + var numberUtil = __webpack_require__(7); + var graphic = __webpack_require__(43); + var colorUtil = __webpack_require__(39); + + var markerHelper = __webpack_require__(349); + + var markAreaTransform = function (seriesModel, coordSys, maModel, item) { + var lt = markerHelper.dataTransform(seriesModel, item[0]); + var rb = markerHelper.dataTransform(seriesModel, item[1]); + var retrieve = zrUtil.retrieve; + + // FIXME make sure lt is less than rb + var ltCoord = lt.coord; + var rbCoord = rb.coord; + ltCoord[0] = retrieve(ltCoord[0], -Infinity); + ltCoord[1] = retrieve(ltCoord[1], -Infinity); + + rbCoord[0] = retrieve(rbCoord[0], Infinity); + rbCoord[1] = retrieve(rbCoord[1], Infinity); + + // Merge option into one + var result = zrUtil.mergeAll([{}, lt, rb]); + + result.coord = [ + lt.coord, rb.coord + ]; + result.x0 = lt.x; + result.y0 = lt.y; + result.x1 = rb.x; + result.y1 = rb.y; + return result; + }; + + function isInifinity(val) { + return !isNaN(val) && !isFinite(val); + } + + // If a markArea has one dim + function ifMarkLineHasOnlyDim(dimIndex, fromCoord, toCoord, coordSys) { + var otherDimIndex = 1 - dimIndex; + return isInifinity(fromCoord[otherDimIndex]) && isInifinity(toCoord[otherDimIndex]); + } + + function markAreaFilter(coordSys, item) { + var fromCoord = item.coord[0]; + var toCoord = item.coord[1]; + if (coordSys.type === 'cartesian2d') { + // In case + // { + // markArea: { + // data: [{ yAxis: 2 }] + // } + // } + if ( + fromCoord && toCoord && + (ifMarkLineHasOnlyDim(1, fromCoord, toCoord, coordSys) + || ifMarkLineHasOnlyDim(0, fromCoord, toCoord, coordSys)) + ) { + return true; + } + } + return markerHelper.dataFilter(coordSys, { + coord: fromCoord, + x: item.x0, + y: item.y0 + }) + || markerHelper.dataFilter(coordSys, { + coord: toCoord, + x: item.x1, + y: item.y1 + }); + } + + // dims can be ['x0', 'y0'], ['x1', 'y1'], ['x0', 'y1'], ['x1', 'y0'] + function getSingleMarkerEndPoint(data, idx, dims, seriesModel, api) { + var coordSys = seriesModel.coordinateSystem; + var itemModel = data.getItemModel(idx); + + var point; + var xPx = numberUtil.parsePercent(itemModel.get(dims[0]), api.getWidth()); + var yPx = numberUtil.parsePercent(itemModel.get(dims[1]), api.getHeight()); + if (!isNaN(xPx) && !isNaN(yPx)) { + point = [xPx, yPx]; + } + else { + // Chart like bar may have there own marker positioning logic + if (seriesModel.getMarkerPosition) { + // Use the getMarkerPoisition + point = seriesModel.getMarkerPosition( + data.getValues(dims, idx) + ); + } + else { + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + point = coordSys.dataToPoint([x, y], true); + } + if (coordSys.type === 'cartesian2d') { + var xAxis = coordSys.getAxis('x'); + var yAxis = coordSys.getAxis('y'); + var x = data.get(dims[0], idx); + var y = data.get(dims[1], idx); + if (isInifinity(x)) { + point[0] = xAxis.toGlobalCoord(xAxis.getExtent()[dims[0] === 'x0' ? 0 : 1]); + } + else if (isInifinity(y)) { + point[1] = yAxis.toGlobalCoord(yAxis.getExtent()[dims[1] === 'y0' ? 0 : 1]); + } + } + + // Use x, y if has any + if (!isNaN(xPx)) { + point[0] = xPx; + } + if (!isNaN(yPx)) { + point[1] = yPx; + } + } + + return point; + } + + var dimPermutations = [['x0', 'y0'], ['x1', 'y0'], ['x1', 'y1'], ['x0', 'y1']]; + + __webpack_require__(350).extend({ + + type: 'markArea', + + updateLayout: function (markAreaModel, ecModel, api) { + ecModel.eachSeries(function (seriesModel) { + var maModel = seriesModel.markAreaModel; + if (maModel) { + var areaData = maModel.getData(); + areaData.each(function (idx) { + var points = zrUtil.map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); + }); + // Layout + areaData.setItemLayout(idx, points); + var el = areaData.getItemGraphicEl(idx); + el.setShape('points', points); + }); + } + }, this); + }, + + renderSeries: function (seriesModel, maModel, ecModel, api) { + var coordSys = seriesModel.coordinateSystem; + var seriesName = seriesModel.name; + var seriesData = seriesModel.getData(); + + var areaGroupMap = this.markerGroupMap; + var polygonGroup = areaGroupMap[seriesName]; + if (!polygonGroup) { + polygonGroup = areaGroupMap[seriesName] = { + group: new graphic.Group() + }; + } + this.group.add(polygonGroup.group); + polygonGroup.__keep = true; + + var areaData = createList(coordSys, seriesModel, maModel); + + // Line data for tooltip and formatter + maModel.setData(areaData); + + // Update visual and layout of line + areaData.each(function (idx) { + // Layout + areaData.setItemLayout(idx, zrUtil.map(dimPermutations, function (dim) { + return getSingleMarkerEndPoint(areaData, idx, dim, seriesModel, api); + })); + + // Visual + areaData.setItemVisual(idx, { + color: seriesData.getVisual('color') + }); + }); + + + areaData.diff(polygonGroup.__data) + .add(function (idx) { + var polygon = new graphic.Polygon({ + shape: { + points: areaData.getItemLayout(idx) + } + }); + areaData.setItemGraphicEl(idx, polygon); + polygonGroup.group.add(polygon); + }) + .update(function (newIdx, oldIdx) { + var polygon = polygonGroup.__data.getItemGraphicEl(oldIdx); + graphic.updateProps(polygon, { + shape: { + points: areaData.getItemLayout(newIdx) + } + }, maModel, newIdx); + polygonGroup.group.add(polygon); + areaData.setItemGraphicEl(newIdx, polygon); + }) + .remove(function (idx) { + var polygon = polygonGroup.__data.getItemGraphicEl(idx); + polygonGroup.group.remove(polygon); + }) + .execute(); + + areaData.eachItemGraphicEl(function (polygon, idx) { + var itemModel = areaData.getItemModel(idx); + var labelModel = itemModel.getModel('label.normal'); + var labelHoverModel = itemModel.getModel('label.emphasis'); + var color = areaData.getItemVisual(idx, 'color'); + polygon.useStyle( + zrUtil.defaults( + itemModel.getModel('itemStyle.normal').getItemStyle(), + { + fill: colorUtil.modifyAlpha(color, 0.4), + stroke: color + } + ) + ); + + polygon.hoverStyle = itemModel.getModel('itemStyle.normal').getItemStyle(); + + var defaultValue = areaData.getName(idx) || ''; + var textColor = color || polygon.style.fill; + graphic.setText(polygon.style, labelModel, textColor); + polygon.style.text = zrUtil.retrieve( + maModel.getFormattedLabel(idx, 'normal'), + defaultValue + ); + + graphic.setText(polygon.hoverStyle, labelHoverModel, textColor); + polygon.hoverStyle.text = zrUtil.retrieve( + maModel.getFormattedLabel(idx, 'emphasis'), + defaultValue + ); + + graphic.setHoverStyle(polygon, {}); + + polygon.dataModel = maModel; + }); + + polygonGroup.__data = areaData; + + polygonGroup.group.silent = maModel.get('silent') || seriesModel.get('silent'); + } + }); + + /** + * @inner + * @param {module:echarts/coord/*} coordSys + * @param {module:echarts/model/Series} seriesModel + * @param {module:echarts/model/Model} mpModel + */ + function createList(coordSys, seriesModel, maModel) { + + var coordDimsInfos; + var areaData; + var dims = ['x0', 'y0', 'x1', 'y1']; + if (coordSys) { + coordDimsInfos = zrUtil.map(coordSys && coordSys.dimensions, function (coordDim) { + var info = seriesModel.getData().getDimensionInfo( + seriesModel.coordDimToDataDim(coordDim)[0] + ) || {}; // In map series data don't have lng and lat dimension. Fallback to same with coordSys + info.name = coordDim; + return info; + }); + areaData = new List(zrUtil.map(dims, function (dim, idx) { + return { + name: dim, + type: coordDimsInfos[idx % 2].type + }; + }), maModel); + } + else { + coordDimsInfos =[{ + name: 'value', + type: 'float' + }]; + areaData = new List(coordDimsInfos, maModel); + } + + var optData = zrUtil.map(maModel.get('data'), zrUtil.curry( + markAreaTransform, seriesModel, coordSys, maModel + )); + if (coordSys) { + optData = zrUtil.filter( + optData, zrUtil.curry(markAreaFilter, coordSys) + ); + } + + var dimValueGetter = coordSys ? function (item, dimName, dataIndex, dimIndex) { + return item.coord[Math.floor(dimIndex / 2)][dimIndex % 2]; + } : function (item) { + return item.value; + }; + areaData.initData(optData, null, dimValueGetter); + areaData.hasItemOption = true; + return areaData; + } + + +/***/ }, +/* 357 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + var echarts = __webpack_require__(1); + + echarts.registerPreprocessor(__webpack_require__(358)); + + __webpack_require__(359); + __webpack_require__(360); + __webpack_require__(361); + __webpack_require__(363); + + + +/***/ }, +/* 358 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline preprocessor + */ + + + var zrUtil = __webpack_require__(4); + + module.exports = function (option) { + var timelineOpt = option && option.timeline; + + if (!zrUtil.isArray(timelineOpt)) { + timelineOpt = timelineOpt ? [timelineOpt] : []; + } + + zrUtil.each(timelineOpt, function (opt) { + if (!opt) { + return; + } + + compatibleEC2(opt); + }); + }; + + function compatibleEC2(opt) { + var type = opt.type; + + var ec2Types = {'number': 'value', 'time': 'time'}; + + // Compatible with ec2 + if (ec2Types[type]) { + opt.axisType = ec2Types[type]; + delete opt.type; + } + + transferItem(opt); + + if (has(opt, 'controlPosition')) { + var controlStyle = opt.controlStyle || (opt.controlStyle = {}); + if (!has(controlStyle, 'position')) { + controlStyle.position = opt.controlPosition; + } + if (controlStyle.position === 'none' && !has(controlStyle, 'show')) { + controlStyle.show = false; + delete controlStyle.position; + } + delete opt.controlPosition; + } + + zrUtil.each(opt.data || [], function (dataItem) { + if (zrUtil.isObject(dataItem) && !zrUtil.isArray(dataItem)) { + if (!has(dataItem, 'value') && has(dataItem, 'name')) { + // In ec2, using name as value. + dataItem.value = dataItem.name; + } + transferItem(dataItem); + } + }); + } + + function transferItem(opt) { + var itemStyle = opt.itemStyle || (opt.itemStyle = {}); + + var itemStyleEmphasis = itemStyle.emphasis || (itemStyle.emphasis = {}); + + // Transfer label out + var label = opt.label || (opt.label || {}); + var labelNormal = label.normal || (label.normal = {}); + var excludeLabelAttr = {normal: 1, emphasis: 1}; + + zrUtil.each(label, function (value, name) { + if (!excludeLabelAttr[name] && !has(labelNormal, name)) { + labelNormal[name] = value; + } + }); + + if (itemStyleEmphasis.label && !has(label, 'emphasis')) { + label.emphasis = itemStyleEmphasis.label; + delete itemStyleEmphasis.label; + } + } + + function has(obj, attr) { + return obj.hasOwnProperty(attr); + } + + + +/***/ }, +/* 359 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(19).registerSubTypeDefaulter('timeline', function () { + // Only slider now. + return 'slider'; + }); + + + +/***/ }, +/* 360 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeilne action + */ + + + var echarts = __webpack_require__(1); + + echarts.registerAction( + + {type: 'timelineChange', event: 'timelineChanged', update: 'prepareAndUpdate'}, + + function (payload, ecModel) { + + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.currentIndex != null) { + timelineModel.setCurrentIndex(payload.currentIndex); + + if (!timelineModel.get('loop', true) && timelineModel.isIndexMax()) { + timelineModel.setPlayState(false); + } + } + + ecModel.resetOption('timeline'); + } + ); + + echarts.registerAction( + + {type: 'timelinePlayChange', event: 'timelinePlayChanged', update: 'update'}, + + function (payload, ecModel) { + var timelineModel = ecModel.getComponent('timeline'); + if (timelineModel && payload.playState != null) { + timelineModel.setPlayState(payload.playState); + } + } + ); + + + +/***/ }, +/* 361 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Silder timeline model + */ + + + var TimelineModel = __webpack_require__(362); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + var SliderTimelineModel = TimelineModel.extend({ + + type: 'timeline.slider', + + /** + * @protected + */ + defaultOption: { + + backgroundColor: 'rgba(0,0,0,0)', // 时间轴背景颜色 + borderColor: '#ccc', // 时间轴边框颜色 + borderWidth: 0, // 时间轴边框线宽,单位px,默认为0(无边框) + + orient: 'horizontal', // 'vertical' + inverse: false, + + tooltip: { // boolean or Object + trigger: 'item' // data item may also have tootip attr. + }, + + symbol: 'emptyCircle', + symbolSize: 10, + + lineStyle: { + show: true, + width: 2, + color: '#304654' + }, + label: { // 文本标签 + position: 'auto', // auto left right top bottom + // When using number, label position is not + // restricted by viewRect. + // positive: right/bottom, negative: left/top + normal: { + show: true, + interval: 'auto', + rotate: 0, + // formatter: null, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#304654' + } + }, + emphasis: { + show: true, + textStyle: { // 其余属性默认使用全局文本样式,详见TEXTSTYLE + color: '#c23531' + } + } + }, + itemStyle: { + normal: { + color: '#304654', + borderWidth: 1 + }, + emphasis: { + color: '#c23531' + } + }, + + checkpointStyle: { + symbol: 'circle', + symbolSize: 13, + color: '#c23531', + borderWidth: 5, + borderColor: 'rgba(194,53,49, 0.5)', + animation: true, + animationDuration: 300, + animationEasing: 'quinticInOut' + }, + + controlStyle: { + show: true, + showPlayBtn: true, + showPrevBtn: true, + showNextBtn: true, + itemSize: 22, + itemGap: 12, + position: 'left', // 'left' 'right' 'top' 'bottom' + playIcon: 'path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z', // jshint ignore:line + stopIcon: 'path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z', // jshint ignore:line + nextIcon: 'path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z', // jshint ignore:line + prevIcon: 'path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z', // jshint ignore:line + normal: { + color: '#304654', + borderColor: '#304654', + borderWidth: 1 + }, + emphasis: { + color: '#c23531', + borderColor: '#c23531', + borderWidth: 2 + } + }, + data: [] + } + + }); + + zrUtil.mixin(SliderTimelineModel, modelUtil.dataFormatMixin); + + module.exports = SliderTimelineModel; + + +/***/ }, +/* 362 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline model + */ + + + var ComponentModel = __webpack_require__(19); + var List = __webpack_require__(97); + var zrUtil = __webpack_require__(4); + var modelUtil = __webpack_require__(5); + + var TimelineModel = ComponentModel.extend({ + + type: 'timeline', + + layoutMode: 'box', + + /** + * @protected + */ + defaultOption: { + + zlevel: 0, // 一级层叠 + z: 4, // 二级层叠 + show: true, + + axisType: 'time', // 模式是时间类型,支持 value, category + + realtime: true, + + left: '20%', + top: null, + right: '20%', + bottom: 0, + width: null, + height: 40, + padding: 5, + + controlPosition: 'left', // 'left' 'right' 'top' 'bottom' 'none' + autoPlay: false, + rewind: false, // 反向播放 + loop: true, + playInterval: 2000, // 播放时间间隔,单位ms + + currentIndex: 0, + + itemStyle: { + normal: {}, + emphasis: {} + }, + label: { + normal: { + textStyle: { + color: '#000' + } + }, + emphasis: {} + }, + + data: [] + }, + + /** + * @override + */ + init: function (option, parentModel, ecModel) { + + /** + * @private + * @type {module:echarts/data/List} + */ + this._data; + + /** + * @private + * @type {Array.} + */ + this._names; + + this.mergeDefaultAndTheme(option, ecModel); + this._initData(); + }, + + /** + * @override + */ + mergeOption: function (option) { + TimelineModel.superApply(this, 'mergeOption', arguments); + this._initData(); + }, + + /** + * @param {number} [currentIndex] + */ + setCurrentIndex: function (currentIndex) { + if (currentIndex == null) { + currentIndex = this.option.currentIndex; + } + var count = this._data.count(); + + if (this.option.loop) { + currentIndex = (currentIndex % count + count) % count; + } + else { + currentIndex >= count && (currentIndex = count - 1); + currentIndex < 0 && (currentIndex = 0); + } + + this.option.currentIndex = currentIndex; + }, + + /** + * @return {number} currentIndex + */ + getCurrentIndex: function () { + return this.option.currentIndex; + }, + + /** + * @return {boolean} + */ + isIndexMax: function () { + return this.getCurrentIndex() >= this._data.count() - 1; + }, + + /** + * @param {boolean} state true: play, false: stop + */ + setPlayState: function (state) { + this.option.autoPlay = !!state; + }, + + /** + * @return {boolean} true: play, false: stop + */ + getPlayState: function () { + return !!this.option.autoPlay; + }, + + /** + * @private + */ + _initData: function () { + var thisOption = this.option; + var dataArr = thisOption.data || []; + var axisType = thisOption.axisType; + var names = this._names = []; + + if (axisType === 'category') { + var idxArr = []; + zrUtil.each(dataArr, function (item, index) { + var value = modelUtil.getDataItemValue(item); + var newItem; + + if (zrUtil.isObject(item)) { + newItem = zrUtil.clone(item); + newItem.value = index; + } + else { + newItem = index; + } + + idxArr.push(newItem); + + if (!zrUtil.isString(value) && (value == null || isNaN(value))) { + value = ''; + } + + names.push(value + ''); + }); + dataArr = idxArr; + } + + var dimType = ({category: 'ordinal', time: 'time'})[axisType] || 'number'; + + var data = this._data = new List([{name: 'value', type: dimType}], this); + + data.initData(dataArr, names); + }, + + getData: function () { + return this._data; + }, + + /** + * @public + * @return {Array.} categoreis + */ + getCategories: function () { + if (this.get('axisType') === 'category') { + return this._names.slice(); + } + } + + }); + + module.exports = TimelineModel; + + +/***/ }, +/* 363 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Silder timeline view + */ + + + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var layout = __webpack_require__(21); + var TimelineView = __webpack_require__(364); + var TimelineAxis = __webpack_require__(365); + var symbolUtil = __webpack_require__(106); + var axisHelper = __webpack_require__(114); + var BoundingRect = __webpack_require__(9); + var matrix = __webpack_require__(11); + var numberUtil = __webpack_require__(7); + var formatUtil = __webpack_require__(6); + var encodeHTML = formatUtil.encodeHTML; + + var bind = zrUtil.bind; + var each = zrUtil.each; + + var PI = Math.PI; + + module.exports = TimelineView.extend({ + + type: 'timeline.slider', + + init: function (ecModel, api) { + + this.api = api; + + /** + * @private + * @type {module:echarts/component/timeline/TimelineAxis} + */ + this._axis; + + /** + * @private + * @type {module:zrender/core/BoundingRect} + */ + this._viewRect; + + /** + * @type {number} + */ + this._timer; + + /** + * @type {module:zrende/Element} + */ + this._currentPointer; + + /** + * @type {module:zrender/container/Group} + */ + this._mainGroup; + + /** + * @type {module:zrender/container/Group} + */ + this._labelGroup; + }, + + /** + * @override + */ + render: function (timelineModel, ecModel, api, payload) { + this.model = timelineModel; + this.api = api; + this.ecModel = ecModel; + + this.group.removeAll(); + + if (timelineModel.get('show', true)) { + + var layoutInfo = this._layout(timelineModel, api); + var mainGroup = this._createGroup('mainGroup'); + var labelGroup = this._createGroup('labelGroup'); + + /** + * @private + * @type {module:echarts/component/timeline/TimelineAxis} + */ + var axis = this._axis = this._createAxis(layoutInfo, timelineModel); + + timelineModel.formatTooltip = function (dataIndex) { + return encodeHTML(axis.scale.getLabel(dataIndex)); + }; + + each( + ['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], + function (name) { + this['_render' + name](layoutInfo, mainGroup, axis, timelineModel); + }, + this + ); + + this._renderAxisLabel(layoutInfo, labelGroup, axis, timelineModel); + + this._position(layoutInfo, timelineModel); + } + + this._doPlayStop(); + }, + + /** + * @override + */ + remove: function () { + this._clearTimer(); + this.group.removeAll(); + }, + + /** + * @override + */ + dispose: function () { + this._clearTimer(); + }, + + _layout: function (timelineModel, api) { + var labelPosOpt = timelineModel.get('label.normal.position'); + var orient = timelineModel.get('orient'); + var viewRect = getViewRect(timelineModel, api); + // Auto label offset. + if (labelPosOpt == null || labelPosOpt === 'auto') { + labelPosOpt = orient === 'horizontal' + ? ((viewRect.y + viewRect.height / 2) < api.getHeight() / 2 ? '-' : '+') + : ((viewRect.x + viewRect.width / 2) < api.getWidth() / 2 ? '+' : '-'); + } + else if (isNaN(labelPosOpt)) { + labelPosOpt = ({ + horizontal: {top: '-', bottom: '+'}, + vertical: {left: '-', right: '+'} + })[orient][labelPosOpt]; + } + + // FIXME + // 暂没有实现用户传入 + // var labelAlign = timelineModel.get('label.normal.textStyle.align'); + // var labelBaseline = timelineModel.get('label.normal.textStyle.baseline'); + var labelAlignMap = { + horizontal: 'center', + vertical: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'left' : 'right' + }; + + var labelBaselineMap = { + horizontal: (labelPosOpt >= 0 || labelPosOpt === '+') ? 'top' : 'bottom', + vertical: 'middle' + }; + var rotationMap = { + horizontal: 0, + vertical: PI / 2 + }; + + // Position + var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width; + + var controlModel = timelineModel.getModel('controlStyle'); + var showControl = controlModel.get('show'); + var controlSize = showControl ? controlModel.get('itemSize') : 0; + var controlGap = showControl ? controlModel.get('itemGap') : 0; + var sizePlusGap = controlSize + controlGap; + + // Special label rotate. + var labelRotation = timelineModel.get('label.normal.rotate') || 0; + labelRotation = labelRotation * PI / 180; // To radian. + + var playPosition; + var prevBtnPosition; + var nextBtnPosition; + var axisExtent; + var controlPosition = controlModel.get('position', true); + var showControl = controlModel.get('show', true); + var showPlayBtn = showControl && controlModel.get('showPlayBtn', true); + var showPrevBtn = showControl && controlModel.get('showPrevBtn', true); + var showNextBtn = showControl && controlModel.get('showNextBtn', true); + var xLeft = 0; + var xRight = mainLength; + + // position[0] means left, position[1] means middle. + if (controlPosition === 'left' || controlPosition === 'bottom') { + showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap); + showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } + else { // 'top' 'right' + showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap); + showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap); + } + axisExtent = [xLeft, xRight]; + + if (timelineModel.get('inverse')) { + axisExtent.reverse(); + } + + return { + viewRect: viewRect, + mainLength: mainLength, + orient: orient, + + rotation: rotationMap[orient], + labelRotation: labelRotation, + labelPosOpt: labelPosOpt, + labelAlign: labelAlignMap[orient], + labelBaseline: labelBaselineMap[orient], + + // Based on mainGroup. + playPosition: playPosition, + prevBtnPosition: prevBtnPosition, + nextBtnPosition: nextBtnPosition, + axisExtent: axisExtent, + + controlSize: controlSize, + controlGap: controlGap + }; + }, + + _position: function (layoutInfo, timelineModel) { + // Position is be called finally, because bounding rect is needed for + // adapt content to fill viewRect (auto adapt offset). + + // Timeline may be not all in the viewRect when 'offset' is specified + // as a number, because it is more appropriate that label aligns at + // 'offset' but not the other edge defined by viewRect. + + var mainGroup = this._mainGroup; + var labelGroup = this._labelGroup; + + var viewRect = layoutInfo.viewRect; + if (layoutInfo.orient === 'vertical') { + // transfrom to horizontal, inverse rotate by left-top point. + var m = matrix.create(); + var rotateOriginX = viewRect.x; + var rotateOriginY = viewRect.y + viewRect.height; + matrix.translate(m, m, [-rotateOriginX, -rotateOriginY]); + matrix.rotate(m, m, -PI / 2); + matrix.translate(m, m, [rotateOriginX, rotateOriginY]); + viewRect = viewRect.clone(); + viewRect.applyTransform(m); + } + + var viewBound = getBound(viewRect); + var mainBound = getBound(mainGroup.getBoundingRect()); + var labelBound = getBound(labelGroup.getBoundingRect()); + + var mainPosition = mainGroup.position; + var labelsPosition = labelGroup.position; + + labelsPosition[0] = mainPosition[0] = viewBound[0][0]; + + var labelPosOpt = layoutInfo.labelPosOpt; + + if (isNaN(labelPosOpt)) { // '+' or '-' + var mainBoundIdx = labelPosOpt === '+' ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx); + } + else { + var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1; + toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx); + labelsPosition[1] = mainPosition[1] + labelPosOpt; + } + + mainGroup.attr('position', mainPosition); + labelGroup.attr('position', labelsPosition); + mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation; + + setOrigin(mainGroup); + setOrigin(labelGroup); + + function setOrigin(targetGroup) { + var pos = targetGroup.position; + targetGroup.origin = [ + viewBound[0][0] - pos[0], + viewBound[1][0] - pos[1] + ]; + } + + function getBound(rect) { + // [[xmin, xmax], [ymin, ymax]] + return [ + [rect.x, rect.x + rect.width], + [rect.y, rect.y + rect.height] + ]; + } + + function toBound(fromPos, from, to, dimIdx, boundIdx) { + fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx]; + } + }, + + _createAxis: function (layoutInfo, timelineModel) { + var data = timelineModel.getData(); + var axisType = timelineModel.get('axisType'); + + var scale = axisHelper.createScaleByModel(timelineModel, axisType); + var dataExtent = data.getDataExtent('value'); + scale.setExtent(dataExtent[0], dataExtent[1]); + this._customizeScale(scale, data); + scale.niceTicks(); + + var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType); + axis.model = timelineModel; + + return axis; + }, + + _customizeScale: function (scale, data) { + + scale.getTicks = function () { + return data.mapArray(['value'], function (value) { + return value; + }); + }; + + scale.getTicksLabels = function () { + return zrUtil.map(this.getTicks(), scale.getLabel, scale); + }; + }, + + _createGroup: function (name) { + var newGroup = this['_' + name] = new graphic.Group(); + this.group.add(newGroup); + return newGroup; + }, + + _renderAxisLine: function (layoutInfo, group, axis, timelineModel) { + var axisExtent = axis.getExtent(); + + if (!timelineModel.get('lineStyle.show')) { + return; + } + + group.add(new graphic.Line({ + shape: { + x1: axisExtent[0], y1: 0, + x2: axisExtent[1], y2: 0 + }, + style: zrUtil.extend( + {lineCap: 'round'}, + timelineModel.getModel('lineStyle').getLineStyle() + ), + silent: true, + z2: 1 + })); + }, + + /** + * @private + */ + _renderAxisTick: function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + var ticks = axis.scale.getTicks(); + + each(ticks, function (value, dataIndex) { + + var tickCoord = axis.dataToCoord(value); + var itemModel = data.getItemModel(dataIndex); + var itemStyleModel = itemModel.getModel('itemStyle.normal'); + var hoverStyleModel = itemModel.getModel('itemStyle.emphasis'); + var symbolOpt = { + position: [tickCoord, 0], + onclick: bind(this._changeTimeline, this, dataIndex) + }; + var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt); + graphic.setHoverStyle(el, hoverStyleModel.getItemStyle()); + + if (itemModel.get('tooltip')) { + el.dataIndex = dataIndex; + el.dataModel = timelineModel; + } + else { + el.dataIndex = el.dataModel = null; + } + + }, this); + }, + + /** + * @private + */ + _renderAxisLabel: function (layoutInfo, group, axis, timelineModel) { + var labelModel = timelineModel.getModel('label.normal'); + + if (!labelModel.get('show')) { + return; + } + + var data = timelineModel.getData(); + var ticks = axis.scale.getTicks(); + var labels = axisHelper.getFormattedLabels( + axis, labelModel.get('formatter') + ); + var labelInterval = axis.getLabelInterval(); + + each(ticks, function (tick, dataIndex) { + if (axis.isLabelIgnored(dataIndex, labelInterval)) { + return; + } + + var itemModel = data.getItemModel(dataIndex); + var itemTextStyleModel = itemModel.getModel('label.normal.textStyle'); + var hoverTextStyleModel = itemModel.getModel('label.emphasis.textStyle'); + var tickCoord = axis.dataToCoord(tick); + var textEl = new graphic.Text({ + style: { + text: labels[dataIndex], + textAlign: layoutInfo.labelAlign, + textVerticalAlign: layoutInfo.labelBaseline, + textFont: itemTextStyleModel.getFont(), + fill: itemTextStyleModel.getTextColor() + }, + position: [tickCoord, 0], + rotation: layoutInfo.labelRotation - layoutInfo.rotation, + onclick: bind(this._changeTimeline, this, dataIndex), + silent: false + }); + + group.add(textEl); + graphic.setHoverStyle(textEl, hoverTextStyleModel.getItemStyle()); + + }, this); + }, + + /** + * @private + */ + _renderControl: function (layoutInfo, group, axis, timelineModel) { + var controlSize = layoutInfo.controlSize; + var rotation = layoutInfo.rotation; + + var itemStyle = timelineModel.getModel('controlStyle.normal').getItemStyle(); + var hoverStyle = timelineModel.getModel('controlStyle.emphasis').getItemStyle(); + var rect = [0, -controlSize / 2, controlSize, controlSize]; + var playState = timelineModel.getPlayState(); + var inverse = timelineModel.get('inverse', true); + + makeBtn( + layoutInfo.nextBtnPosition, + 'controlStyle.nextIcon', + bind(this._changeTimeline, this, inverse ? '-' : '+') + ); + makeBtn( + layoutInfo.prevBtnPosition, + 'controlStyle.prevIcon', + bind(this._changeTimeline, this, inverse ? '+' : '-') + ); + makeBtn( + layoutInfo.playPosition, + 'controlStyle.' + (playState ? 'stopIcon' : 'playIcon'), + bind(this._handlePlayClick, this, !playState), + true + ); + + function makeBtn(position, iconPath, onclick, willRotate) { + if (!position) { + return; + } + var opt = { + position: position, + origin: [controlSize / 2, 0], + rotation: willRotate ? -rotation : 0, + rectHover: true, + style: itemStyle, + onclick: onclick + }; + var btn = makeIcon(timelineModel, iconPath, rect, opt); + group.add(btn); + graphic.setHoverStyle(btn, hoverStyle); + } + }, + + _renderCurrentPointer: function (layoutInfo, group, axis, timelineModel) { + var data = timelineModel.getData(); + var currentIndex = timelineModel.getCurrentIndex(); + var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle'); + var me = this; + + var callback = { + onCreate: function (pointer) { + pointer.draggable = true; + pointer.drift = bind(me._handlePointerDrag, me); + pointer.ondragend = bind(me._handlePointerDragend, me); + pointerMoveTo(pointer, currentIndex, axis, timelineModel, true); + }, + onUpdate: function (pointer) { + pointerMoveTo(pointer, currentIndex, axis, timelineModel); + } + }; + + // Reuse when exists, for animation and drag. + this._currentPointer = giveSymbol( + pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback + ); + }, + + _handlePlayClick: function (nextState) { + this._clearTimer(); + this.api.dispatchAction({ + type: 'timelinePlayChange', + playState: nextState, + from: this.uid + }); + }, + + _handlePointerDrag: function (dx, dy, e) { + this._clearTimer(); + this._pointerChangeTimeline([e.offsetX, e.offsetY]); + }, + + _handlePointerDragend: function (e) { + this._pointerChangeTimeline([e.offsetX, e.offsetY], true); + }, + + _pointerChangeTimeline: function (mousePos, trigger) { + var toCoord = this._toAxisCoord(mousePos)[0]; + + var axis = this._axis; + var axisExtent = numberUtil.asc(axis.getExtent().slice()); + + toCoord > axisExtent[1] && (toCoord = axisExtent[1]); + toCoord < axisExtent[0] && (toCoord = axisExtent[0]); + + this._currentPointer.position[0] = toCoord; + this._currentPointer.dirty(); + + var targetDataIndex = this._findNearestTick(toCoord); + var timelineModel = this.model; + + if (trigger || ( + targetDataIndex !== timelineModel.getCurrentIndex() + && timelineModel.get('realtime') + )) { + this._changeTimeline(targetDataIndex); + } + }, + + _doPlayStop: function () { + this._clearTimer(); + + if (this.model.getPlayState()) { + this._timer = setTimeout( + bind(handleFrame, this), + this.model.get('playInterval') + ); + } + + function handleFrame() { + // Do not cache + var timelineModel = this.model; + this._changeTimeline( + timelineModel.getCurrentIndex() + + (timelineModel.get('rewind', true) ? -1 : 1) + ); + } + }, + + _toAxisCoord: function (vertex) { + var trans = this._mainGroup.getLocalTransform(); + return graphic.applyTransform(vertex, trans, true); + }, + + _findNearestTick: function (axisCoord) { + var data = this.model.getData(); + var dist = Infinity; + var targetDataIndex; + var axis = this._axis; + + data.each(['value'], function (value, dataIndex) { + var coord = axis.dataToCoord(value); + var d = Math.abs(coord - axisCoord); + if (d < dist) { + dist = d; + targetDataIndex = dataIndex; + } + }); + + return targetDataIndex; + }, + + _clearTimer: function () { + if (this._timer) { + clearTimeout(this._timer); + this._timer = null; + } + }, + + _changeTimeline: function (nextIndex) { + var currentIndex = this.model.getCurrentIndex(); + + if (nextIndex === '+') { + nextIndex = currentIndex + 1; + } + else if (nextIndex === '-') { + nextIndex = currentIndex - 1; + } + + this.api.dispatchAction({ + type: 'timelineChange', + currentIndex: nextIndex, + from: this.uid + }); + } + + }); + + function getViewRect(model, api) { + return layout.getLayoutRect( + model.getBoxLayoutParams(), + { + width: api.getWidth(), + height: api.getHeight() + }, + model.get('padding') + ); + } + + function makeIcon(timelineModel, objPath, rect, opts) { + var icon = graphic.makePath( + timelineModel.get(objPath).replace(/^path:\/\//, ''), + zrUtil.clone(opts || {}), + new BoundingRect(rect[0], rect[1], rect[2], rect[3]), + 'center' + ); + + return icon; + } + + /** + * Create symbol or update symbol + */ + function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) { + var symbolType = hostModel.get('symbol'); + var color = itemStyleModel.get('color'); + var symbolSize = hostModel.get('symbolSize'); + var halfSymbolSize = symbolSize / 2; + var itemStyle = itemStyleModel.getItemStyle(['color', 'symbol', 'symbolSize']); + + if (!symbol) { + symbol = symbolUtil.createSymbol( + symbolType, -halfSymbolSize, -halfSymbolSize, symbolSize, symbolSize, color + ); + group.add(symbol); + callback && callback.onCreate(symbol); + } + else { + symbol.setStyle(itemStyle); + symbol.setColor(color); + group.add(symbol); // Group may be new, also need to add. + callback && callback.onUpdate(symbol); + } + + opt = zrUtil.merge({ + rectHover: true, + style: itemStyle, + z2: 100 + }, opt, true); + + symbol.attr(opt); + + return symbol; + } + + function pointerMoveTo(pointer, dataIndex, axis, timelineModel, noAnimation) { + if (pointer.dragging) { + return; + } + + var pointerModel = timelineModel.getModel('checkpointStyle'); + var toCoord = axis.dataToCoord(timelineModel.getData().get(['value'], dataIndex)); + + if (noAnimation || !pointerModel.get('animation', true)) { + pointer.attr({position: [toCoord, 0]}); + } + else { + pointer.stopAnimation(true); + pointer.animateTo( + {position: [toCoord, 0]}, + pointerModel.get('animationDuration', true), + pointerModel.get('animationEasing', true) + ); + } + } + + + +/***/ }, +/* 364 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Timeline view + */ + + + // var zrUtil = require('zrender/lib/core/util'); + // var graphic = require('../../util/graphic'); + var ComponentView = __webpack_require__(29); + + module.exports = ComponentView.extend({ + + type: 'timeline' + }); + + + +/***/ }, +/* 365 */ +/***/ function(module, exports, __webpack_require__) { + + + + var zrUtil = __webpack_require__(4); + var Axis = __webpack_require__(123); + var axisHelper = __webpack_require__(114); + + /** + * Extend axis 2d + * @constructor module:echarts/coord/cartesian/Axis2D + * @extends {module:echarts/coord/cartesian/Axis} + * @param {string} dim + * @param {*} scale + * @param {Array.} coordExtent + * @param {string} axisType + * @param {string} position + */ + var TimelineAxis = function (dim, scale, coordExtent, axisType) { + + Axis.call(this, dim, scale, coordExtent); + + /** + * Axis type + * - 'category' + * - 'value' + * - 'time' + * - 'log' + * @type {string} + */ + this.type = axisType || 'value'; + + /** + * @private + * @type {number} + */ + this._autoLabelInterval; + + /** + * Axis model + * @param {module:echarts/component/TimelineModel} + */ + this.model = null; + }; + + TimelineAxis.prototype = { + + constructor: TimelineAxis, + + /** + * @public + * @return {number} + */ + getLabelInterval: function () { + var timelineModel = this.model; + var labelModel = timelineModel.getModel('label.normal'); + var labelInterval = labelModel.get('interval'); + + if (labelInterval != null && labelInterval != 'auto') { + return labelInterval; + } + + var labelInterval = this._autoLabelInterval; + + if (!labelInterval) { + labelInterval = this._autoLabelInterval = axisHelper.getAxisLabelInterval( + zrUtil.map(this.scale.getTicks(), this.dataToCoord, this), + axisHelper.getFormattedLabels(this, labelModel.get('formatter')), + labelModel.getModel('textStyle').getFont(), + timelineModel.get('orient') === 'horizontal' + ); + } + + return labelInterval; + }, + + /** + * If label is ignored. + * Automatically used when axis is category and label can not be all shown + * @public + * @param {number} idx + * @return {boolean} + */ + isLabelIgnored: function (idx) { + if (this.type === 'category') { + var labelInterval = this.getLabelInterval(); + return ((typeof labelInterval === 'function') + && !labelInterval(idx, this.scale.getLabel(idx))) + || idx % (labelInterval + 1); + } + } + + }; + + zrUtil.inherits(TimelineAxis, Axis); + + module.exports = TimelineAxis; + + +/***/ }, +/* 366 */ +/***/ function(module, exports, __webpack_require__) { + + + + __webpack_require__(367); + __webpack_require__(368); + + __webpack_require__(370); + __webpack_require__(371); + __webpack_require__(372); + __webpack_require__(373); + __webpack_require__(378); + + +/***/ }, +/* 367 */ +/***/ function(module, exports, __webpack_require__) { + + + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + + var ToolboxModel = __webpack_require__(1).extendComponentModel({ + + type: 'toolbox', + + layoutMode: { + type: 'box', + ignoreSize: true + }, + + mergeDefaultAndTheme: function (option) { + ToolboxModel.superApply(this, 'mergeDefaultAndTheme', arguments); + + zrUtil.each(this.option.feature, function (featureOpt, featureName) { + var Feature = featureManager.get(featureName); + Feature && zrUtil.merge(featureOpt, Feature.defaultOption); + }); + }, + + defaultOption: { + + show: true, + + z: 6, + + zlevel: 0, + + orient: 'horizontal', + + left: 'right', + + top: 'top', + + // right + // bottom + + backgroundColor: 'transparent', + + borderColor: '#ccc', + + borderWidth: 0, + + padding: 5, + + itemSize: 15, + + itemGap: 8, + + showTitle: true, + + iconStyle: { + normal: { + borderColor: '#666', + color: 'none' + }, + emphasis: { + borderColor: '#3E98C5' + } + } + // textStyle: {}, + + // feature + } + }); + + module.exports = ToolboxModel; + + +/***/ }, +/* 368 */ +/***/ function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) { + + var featureManager = __webpack_require__(314); + var zrUtil = __webpack_require__(4); + var graphic = __webpack_require__(43); + var Model = __webpack_require__(12); + var DataDiffer = __webpack_require__(98); + var listComponentHelper = __webpack_require__(277); + var textContain = __webpack_require__(8); + + module.exports = __webpack_require__(1).extendComponentView({ + + type: 'toolbox', + + render: function (toolboxModel, ecModel, api, payload) { + var group = this.group; + group.removeAll(); + + if (!toolboxModel.get('show')) { + return; + } + + var itemSize = +toolboxModel.get('itemSize'); + var featureOpts = toolboxModel.get('feature') || {}; + var features = this._features || (this._features = {}); + + var featureNames = []; + zrUtil.each(featureOpts, function (opt, name) { + featureNames.push(name); + }); + + (new DataDiffer(this._featureNames || [], featureNames)) + .add(process) + .update(process) + .remove(zrUtil.curry(process, null)) + .execute(); + + // Keep for diff. + this._featureNames = featureNames; + + function process(newIndex, oldIndex) { + var featureName = featureNames[newIndex]; + var oldName = featureNames[oldIndex]; + var featureOpt = featureOpts[featureName]; + var featureModel = new Model(featureOpt, toolboxModel, toolboxModel.ecModel); + var feature; + + if (featureName && !oldName) { // Create + if (isUserFeatureName(featureName)) { + feature = { + model: featureModel, + onclick: featureModel.option.onclick, + featureName: featureName + }; + } + else { + var Feature = featureManager.get(featureName); + if (!Feature) { + return; + } + feature = new Feature(featureModel, ecModel, api); + } + features[featureName] = feature; + } + else { + feature = features[oldName]; + // If feature does not exsit. + if (!feature) { + return; + } + feature.model = featureModel; + feature.ecModel = ecModel; + feature.api = api; + } + + if (!featureName && oldName) { + feature.dispose && feature.dispose(ecModel, api); + return; + } + + if (!featureModel.get('show') || feature.unusable) { + feature.remove && feature.remove(ecModel, api); + return; + } + + createIconPaths(featureModel, feature, featureName); + + featureModel.setIconStatus = function (iconName, status) { + var option = this.option; + var iconPaths = this.iconPaths; + option.iconStatus = option.iconStatus || {}; + option.iconStatus[iconName] = status; + // FIXME + iconPaths[iconName] && iconPaths[iconName].trigger(status); + }; + + if (feature.render) { + feature.render(featureModel, ecModel, api, payload); + } + } + + function createIconPaths(featureModel, feature, featureName) { + var iconStyleModel = featureModel.getModel('iconStyle'); + + // If one feature has mutiple icon. they are orginaized as + // { + // icon: { + // foo: '', + // bar: '' + // }, + // title: { + // foo: '', + // bar: '' + // } + // } + var icons = feature.getIcons ? feature.getIcons() : featureModel.get('icon'); + var titles = featureModel.get('title') || {}; + if (typeof icons === 'string') { + var icon = icons; + var title = titles; + icons = {}; + titles = {}; + icons[featureName] = icon; + titles[featureName] = title; + } + var iconPaths = featureModel.iconPaths = {}; + zrUtil.each(icons, function (icon, iconName) { + var normalStyle = iconStyleModel.getModel('normal').getItemStyle(); + var hoverStyle = iconStyleModel.getModel('emphasis').getItemStyle(); + + var style = { + x: -itemSize / 2, + y: -itemSize / 2, + width: itemSize, + height: itemSize + }; + var path = icon.indexOf('image://') === 0 + ? ( + style.image = icon.slice(8), + new graphic.Image({style: style}) + ) + : graphic.makePath( + icon.replace('path://', ''), + { + style: normalStyle, + hoverStyle: hoverStyle, + rectHover: true + }, + style, + 'center' + ); + + graphic.setHoverStyle(path); + + if (toolboxModel.get('showTitle')) { + path.__title = titles[iconName]; + path.on('mouseover', function () { + path.setStyle({ + text: titles[iconName], + textPosition: hoverStyle.textPosition || 'bottom', + textFill: hoverStyle.fill || hoverStyle.stroke || '#000', + textAlign: hoverStyle.textAlign || 'center' + }); + }) + .on('mouseout', function () { + path.setStyle({ + textFill: null + }); + }); + } + path.trigger(featureModel.get('iconStatus.' + iconName) || 'normal'); + + group.add(path); + path.on('click', zrUtil.bind( + feature.onclick, feature, ecModel, api, iconName + )); + + iconPaths[iconName] = path; + }); + } + + listComponentHelper.layout(group, toolboxModel, api); + // Render background after group is layout + // FIXME + listComponentHelper.addBackground(group, toolboxModel); + + // Adjust icon title positions to avoid them out of screen + group.eachChild(function (icon) { + var titleText = icon.__title; + var hoverStyle = icon.hoverStyle; + // May be background element + if (hoverStyle && titleText) { + var rect = textContain.getBoundingRect( + titleText, hoverStyle.font + ); + var offsetX = icon.position[0] + group.position[0]; + var offsetY = icon.position[1] + group.position[1] + itemSize; + + var needPutOnTop = false; + if (offsetY + rect.height > api.getHeight()) { + hoverStyle.textPosition = 'top'; + needPutOnTop = true; + } + var topOffset = needPutOnTop ? (-5 - rect.height) : (itemSize + 8); + if (offsetX + rect.width / 2 > api.getWidth()) { + hoverStyle.textPosition = ['100%', topOffset]; + hoverStyle.textAlign = 'right'; + } + else if (offsetX - rect.width / 2 < 0) { + hoverStyle.textPosition = [0, topOffset]; + hoverStyle.textAlign = 'left'; + } + } + }); + }, + + updateView: function (toolboxModel, ecModel, api, payload) { + zrUtil.each(this._features, function (feature) { + feature.updateView && feature.updateView(feature.model, ecModel, api, payload); + }); + }, + + updateLayout: function (toolboxModel, ecModel, api, payload) { + zrUtil.each(this._features, function (feature) { + feature.updateLayout && feature.updateLayout(feature.model, ecModel, api, payload); + }); + }, + + remove: function (ecModel, api) { + zrUtil.each(this._features, function (feature) { + feature.remove && feature.remove(ecModel, api); + }); + this.group.removeAll(); + }, + + dispose: function (ecModel, api) { + zrUtil.each(this._features, function (feature) { + feature.dispose && feature.dispose(ecModel, api); + }); + } + }); + + function isUserFeatureName(featureName) { + return featureName.indexOf('my') === 0; + } + + + /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(369))) + +/***/ }, +/* 369 */ +/***/ function(module, exports) { + + // shim for using process in browser + + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + (function () { + try { + cachedSetTimeout = setTimeout; + } catch (e) { + cachedSetTimeout = function () { + throw new Error('setTimeout is not defined'); + } + } + try { + cachedClearTimeout = clearTimeout; + } catch (e) { + cachedClearTimeout = function () { + throw new Error('clearTimeout is not defined'); + } + } + } ()) + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = cachedSetTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + cachedClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + cachedSetTimeout(drainQueue, 0); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + +/***/ }, +/* 370 */ +/***/ function(module, exports, __webpack_require__) { + + + + var env = __webpack_require__(2); + + function SaveAsImage (model) { + this.model = model; + } + + SaveAsImage.defaultOption = { + show: true, + icon: 'M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0', + title: '保存为图片', + type: 'png', + // Default use option.backgroundColor + // backgroundColor: '#fff', + name: '', + excludeComponents: ['toolbox'], + pixelRatio: 1, + lang: ['右键另存为图片'] + }; + + SaveAsImage.prototype.unusable = !env.canvasSupported; + + var proto = SaveAsImage.prototype; + + proto.onclick = function (ecModel, api) { + var model = this.model; + var title = model.get('name') || ecModel.get('title.0.text') || 'echarts'; + var $a = document.createElement('a'); + var type = model.get('type', true) || 'png'; + $a.download = title + '.' + type; + $a.target = '_blank'; + var url = api.getConnectedDataURL({ + type: type, + backgroundColor: model.get('backgroundColor', true) + || ecModel.get('backgroundColor') || '#fff', + excludeComponents: model.get('excludeComponents'), + pixelRatio: model.get('pixelRatio') + }); + $a.href = url; + // Chrome and Firefox + if (typeof MouseEvent === 'function') { + var evt = new MouseEvent('click', { + view: window, + bubbles: true, + cancelable: false + }); + $a.dispatchEvent(evt); + } + // IE + else { + var lang = model.get('lang'); + var html = '' + + '' + + '' + + ''; + var tab = window.open(); + tab.document.write(html); + } + }; + + __webpack_require__(314).register( + 'saveAsImage', SaveAsImage + ); + + module.exports = SaveAsImage; + + +/***/ }, +/* 371 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + + function MagicType(model) { + this.model = model; + } + + MagicType.defaultOption = { + show: true, + type: [], + // Icon group + icon: { + line: 'M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4', + bar: 'M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7', + stack: 'M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z', // jshint ignore:line + tiled: 'M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z' + }, + title: { + line: '切换为折线图', + bar: '切换为柱状图', + stack: '切换为堆叠', + tiled: '切换为平铺' + }, + option: {}, + seriesIndex: {} + }; + + var proto = MagicType.prototype; + + proto.getIcons = function () { + var model = this.model; + var availableIcons = model.get('icon'); + var icons = {}; + zrUtil.each(model.get('type'), function (type) { + if (availableIcons[type]) { + icons[type] = availableIcons[type]; + } + }); + return icons; + }; + + var seriesOptGenreator = { + 'line': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + type: 'line', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get('option.line') || {}, true); + } + }, + 'bar': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line') { + return zrUtil.merge({ + id: seriesId, + type: 'bar', + // Preserve data related option + data: seriesModel.get('data'), + stack: seriesModel.get('stack'), + markPoint: seriesModel.get('markPoint'), + markLine: seriesModel.get('markLine') + }, model.get('option.bar') || {}, true); + } + }, + 'stack': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line' || seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + stack: '__ec_magicType_stack__' + }, model.get('option.stack') || {}, true); + } + }, + 'tiled': function (seriesType, seriesId, seriesModel, model) { + if (seriesType === 'line' || seriesType === 'bar') { + return zrUtil.merge({ + id: seriesId, + stack: '' + }, model.get('option.tiled') || {}, true); + } + } + }; + + var radioTypes = [ + ['line', 'bar'], + ['stack', 'tiled'] + ]; + + proto.onclick = function (ecModel, api, type) { + var model = this.model; + var seriesIndex = model.get('seriesIndex.' + type); + // Not supported magicType + if (!seriesOptGenreator[type]) { + return; + } + var newOption = { + series: [] + }; + var generateNewSeriesTypes = function (seriesModel) { + var seriesType = seriesModel.subType; + var seriesId = seriesModel.id; + var newSeriesOpt = seriesOptGenreator[type]( + seriesType, seriesId, seriesModel, model + ); + if (newSeriesOpt) { + // PENDING If merge original option? + zrUtil.defaults(newSeriesOpt, seriesModel.option); + newOption.series.push(newSeriesOpt); + } + // Modify boundaryGap + var coordSys = seriesModel.coordinateSystem; + if (coordSys && coordSys.type === 'cartesian2d' && (type === 'line' || type === 'bar')) { + var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; + if (categoryAxis) { + var axisDim = categoryAxis.dim; + var axisType = axisDim + 'Axis'; + var axisModel = ecModel.queryComponents({ + mainType: axisType, + index: seriesModel.get(name + 'Index'), + id: seriesModel.get(name + 'Id') + })[0]; + var axisIndex = axisModel.componentIndex; + + newOption[axisType] = newOption[axisType] || []; + for (var i = 0; i <= axisIndex; i++) { + newOption[axisType][axisIndex] = newOption[axisType][axisIndex] || {}; + } + newOption[axisType][axisIndex].boundaryGap = type === 'bar' ? true : false; + } + } + }; + + zrUtil.each(radioTypes, function (radio) { + if (zrUtil.indexOf(radio, type) >= 0) { + zrUtil.each(radio, function (item) { + model.setIconStatus(item, 'normal'); + }); + } + }); + + model.setIconStatus(type, 'emphasis'); + + ecModel.eachComponent( + { + mainType: 'series', + query: seriesIndex == null ? null : { + seriesIndex: seriesIndex + } + }, generateNewSeriesTypes + ); + api.dispatchAction({ + type: 'changeMagicType', + currentType: type, + newOption: newOption + }); + }; + + var echarts = __webpack_require__(1); + echarts.registerAction({ + type: 'changeMagicType', + event: 'magicTypeChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + ecModel.mergeOption(payload.newOption); + }); + + __webpack_require__(314).register('magicType', MagicType); + + module.exports = MagicType; + + +/***/ }, +/* 372 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @module echarts/component/toolbox/feature/DataView + */ + + + + var zrUtil = __webpack_require__(4); + var eventTool = __webpack_require__(87); + + + var BLOCK_SPLITER = new Array(60).join('-'); + var ITEM_SPLITER = '\t'; + /** + * Group series into two types + * 1. on category axis, like line, bar + * 2. others, like scatter, pie + * @param {module:echarts/model/Global} ecModel + * @return {Object} + * @inner + */ + function groupSeries(ecModel) { + var seriesGroupByCategoryAxis = {}; + var otherSeries = []; + var meta = []; + ecModel.eachRawSeries(function (seriesModel) { + var coordSys = seriesModel.coordinateSystem; + + if (coordSys && (coordSys.type === 'cartesian2d' || coordSys.type === 'polar')) { + var baseAxis = coordSys.getBaseAxis(); + if (baseAxis.type === 'category') { + var key = baseAxis.dim + '_' + baseAxis.index; + if (!seriesGroupByCategoryAxis[key]) { + seriesGroupByCategoryAxis[key] = { + categoryAxis: baseAxis, + valueAxis: coordSys.getOtherAxis(baseAxis), + series: [] + }; + meta.push({ + axisDim: baseAxis.dim, + axisIndex: baseAxis.index + }); + } + seriesGroupByCategoryAxis[key].series.push(seriesModel); + } + else { + otherSeries.push(seriesModel); + } + } + else { + otherSeries.push(seriesModel); + } + }); + + return { + seriesGroupByCategoryAxis: seriesGroupByCategoryAxis, + other: otherSeries, + meta: meta + }; + } + + /** + * Assemble content of series on cateogory axis + * @param {Array.} series + * @return {string} + * @inner + */ + function assembleSeriesWithCategoryAxis(series) { + var tables = []; + zrUtil.each(series, function (group, key) { + var categoryAxis = group.categoryAxis; + var valueAxis = group.valueAxis; + var valueAxisDim = valueAxis.dim; + + var headers = [' '].concat(zrUtil.map(group.series, function (series) { + return series.name; + })); + var columns = [categoryAxis.model.getCategories()]; + zrUtil.each(group.series, function (series) { + columns.push(series.getRawData().mapArray(valueAxisDim, function (val) { + return val; + })); + }); + // Assemble table content + var lines = [headers.join(ITEM_SPLITER)]; + for (var i = 0; i < columns[0].length; i++) { + var items = []; + for (var j = 0; j < columns.length; j++) { + items.push(columns[j][i]); + } + lines.push(items.join(ITEM_SPLITER)); + } + tables.push(lines.join('\n')); + }); + return tables.join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + + /** + * Assemble content of other series + * @param {Array.} series + * @return {string} + * @inner + */ + function assembleOtherSeries(series) { + return zrUtil.map(series, function (series) { + var data = series.getRawData(); + var lines = [series.name]; + var vals = []; + data.each(data.dimensions, function () { + var argLen = arguments.length; + var dataIndex = arguments[argLen - 1]; + var name = data.getName(dataIndex); + for (var i = 0; i < argLen - 1; i++) { + vals[i] = arguments[i]; + } + lines.push((name ? (name + ITEM_SPLITER) : '') + vals.join(ITEM_SPLITER)); + }); + return lines.join('\n'); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'); + } + + /** + * @param {module:echarts/model/Global} + * @return {string} + * @inner + */ + function getContentFromModel(ecModel) { + + var result = groupSeries(ecModel); + + return { + value: zrUtil.filter([ + assembleSeriesWithCategoryAxis(result.seriesGroupByCategoryAxis), + assembleOtherSeries(result.other) + ], function (str) { + return str.replace(/[\n\t\s]/g, ''); + }).join('\n\n' + BLOCK_SPLITER + '\n\n'), + + meta: result.meta + }; + } + + + function trim(str) { + return str.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); + } + /** + * If a block is tsv format + */ + function isTSVFormat(block) { + // Simple method to find out if a block is tsv format + var firstLine = block.slice(0, block.indexOf('\n')); + if (firstLine.indexOf(ITEM_SPLITER) >= 0) { + return true; + } + } + + var itemSplitRegex = new RegExp('[' + ITEM_SPLITER + ']+', 'g'); + /** + * @param {string} tsv + * @return {Array.} + */ + function parseTSVContents(tsv) { + var tsvLines = tsv.split(/\n+/g); + var headers = trim(tsvLines.shift()).split(itemSplitRegex); + + var categories = []; + var series = zrUtil.map(headers, function (header) { + return { + name: header, + data: [] + }; + }); + for (var i = 0; i < tsvLines.length; i++) { + var items = trim(tsvLines[i]).split(itemSplitRegex); + categories.push(items.shift()); + for (var j = 0; j < items.length; j++) { + series[j] && (series[j].data[i] = items[j]); + } + } + return { + series: series, + categories: categories + }; + } + + /** + * @param {string} str + * @return {Array.} + * @inner + */ + function parseListContents(str) { + var lines = str.split(/\n+/g); + var seriesName = trim(lines.shift()); + + var data = []; + for (var i = 0; i < lines.length; i++) { + var items = trim(lines[i]).split(itemSplitRegex); + var name = ''; + var value; + var hasName = false; + if (isNaN(items[0])) { // First item is name + hasName = true; + name = items[0]; + items = items.slice(1); + data[i] = { + name: name, + value: [] + }; + value = data[i].value; + } + else { + value = data[i] = []; + } + for (var j = 0; j < items.length; j++) { + value.push(+items[j]); + } + if (value.length === 1) { + hasName ? (data[i].value = value[0]) : (data[i] = value[0]); + } + } + + return { + name: seriesName, + data: data + }; + } + + /** + * @param {string} str + * @param {Array.} blockMetaList + * @return {Object} + * @inner + */ + function parseContents(str, blockMetaList) { + var blocks = str.split(new RegExp('\n*' + BLOCK_SPLITER + '\n*', 'g')); + var newOption = { + series: [] + }; + zrUtil.each(blocks, function (block, idx) { + if (isTSVFormat(block)) { + var result = parseTSVContents(block); + var blockMeta = blockMetaList[idx]; + var axisKey = blockMeta.axisDim + 'Axis'; + + if (blockMeta) { + newOption[axisKey] = newOption[axisKey] || []; + newOption[axisKey][blockMeta.axisIndex] = { + data: result.categories + }; + newOption.series = newOption.series.concat(result.series); + } + } + else { + var result = parseListContents(block); + newOption.series.push(result); + } + }); + return newOption; + } + + /** + * @alias {module:echarts/component/toolbox/feature/DataView} + * @constructor + * @param {module:echarts/model/Model} model + */ + function DataView(model) { + + this._dom = null; + + this.model = model; + } + + DataView.defaultOption = { + show: true, + readOnly: false, + optionToContent: null, + contentToOption: null, + + icon: 'M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28', + title: '数据视图', + lang: ['数据视图', '关闭', '刷新'], + backgroundColor: '#fff', + textColor: '#000', + textareaColor: '#fff', + textareaBorderColor: '#333', + buttonColor: '#c23531', + buttonTextColor: '#fff' + }; + + DataView.prototype.onclick = function (ecModel, api) { + var container = api.getDom(); + var model = this.model; + if (this._dom) { + container.removeChild(this._dom); + } + var root = document.createElement('div'); + root.style.cssText = 'position:absolute;left:5px;top:5px;bottom:5px;right:5px;'; + root.style.backgroundColor = model.get('backgroundColor') || '#fff'; + + // Create elements + var header = document.createElement('h4'); + var lang = model.get('lang') || []; + header.innerHTML = lang[0] || model.get('title'); + header.style.cssText = 'margin: 10px 20px;'; + header.style.color = model.get('textColor'); + + var viewMain = document.createElement('div'); + var textarea = document.createElement('textarea'); + viewMain.style.cssText = 'display:block;width:100%;overflow:hidden;'; + + var optionToContent = model.get('optionToContent'); + var contentToOption = model.get('contentToOption'); + var result = getContentFromModel(ecModel); + if (typeof optionToContent === 'function') { + var htmlOrDom = optionToContent(api.getOption()); + if (typeof htmlOrDom === 'string') { + viewMain.innerHTML = htmlOrDom; + } + else if (zrUtil.isDom(htmlOrDom)) { + viewMain.appendChild(htmlOrDom); + } + } + else { + // Use default textarea + viewMain.appendChild(textarea); + textarea.readOnly = model.get('readOnly'); + textarea.style.cssText = 'width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;'; + textarea.style.color = model.get('textColor'); + textarea.style.borderColor = model.get('textareaBorderColor'); + textarea.style.backgroundColor = model.get('textareaColor'); + textarea.value = result.value; + } + + var blockMetaList = result.meta; + + var buttonContainer = document.createElement('div'); + buttonContainer.style.cssText = 'position:absolute;bottom:0;left:0;right:0;'; + + var buttonStyle = 'float:right;margin-right:20px;border:none;' + + 'cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px'; + var closeButton = document.createElement('div'); + var refreshButton = document.createElement('div'); + + buttonStyle += ';background-color:' + model.get('buttonColor'); + buttonStyle += ';color:' + model.get('buttonTextColor'); + + var self = this; + + function close() { + container.removeChild(root); + self._dom = null; + } + eventTool.addEventListener(closeButton, 'click', close); + + eventTool.addEventListener(refreshButton, 'click', function () { + var newOption; + try { + if (typeof contentToOption === 'function') { + newOption = contentToOption(viewMain, api.getOption()); + } + else { + newOption = parseContents(textarea.value, blockMetaList); + } + } + catch (e) { + close(); + throw new Error('Data view format error ' + e); + } + if (newOption) { + api.dispatchAction({ + type: 'changeDataView', + newOption: newOption + }); + } + + close(); + }); + + closeButton.innerHTML = lang[1]; + refreshButton.innerHTML = lang[2]; + refreshButton.style.cssText = buttonStyle; + closeButton.style.cssText = buttonStyle; + + !model.get('readOnly') && buttonContainer.appendChild(refreshButton); + buttonContainer.appendChild(closeButton); + + // http://stackoverflow.com/questions/6637341/use-tab-to-indent-in-textarea + eventTool.addEventListener(textarea, 'keydown', function (e) { + if ((e.keyCode || e.which) === 9) { + // get caret position/selection + var val = this.value; + var start = this.selectionStart; + var end = this.selectionEnd; + + // set textarea value to: text before caret + tab + text after caret + this.value = val.substring(0, start) + ITEM_SPLITER + val.substring(end); + + // put caret at right position again + this.selectionStart = this.selectionEnd = start + 1; + + // prevent the focus lose + eventTool.stop(e); + } + }); + + root.appendChild(header); + root.appendChild(viewMain); + root.appendChild(buttonContainer); + + viewMain.style.height = (container.clientHeight - 80) + 'px'; + + container.appendChild(root); + this._dom = root; + }; + + DataView.prototype.remove = function (ecModel, api) { + this._dom && api.getDom().removeChild(this._dom); + }; + + DataView.prototype.dispose = function (ecModel, api) { + this.remove(ecModel, api); + }; + + /** + * @inner + */ + function tryMergeDataOption(newData, originalData) { + return zrUtil.map(newData, function (newVal, idx) { + var original = originalData && originalData[idx]; + if (zrUtil.isObject(original) && !zrUtil.isArray(original)) { + if (zrUtil.isObject(newVal) && !zrUtil.isArray(newVal)) { + newVal = newVal.value; + } + // Original data has option + return zrUtil.defaults({ + value: newVal + }, original); + } + else { + return newVal; + } + }); + } + + __webpack_require__(314).register('dataView', DataView); + + __webpack_require__(1).registerAction({ + type: 'changeDataView', + event: 'dataViewChanged', + update: 'prepareAndUpdate' + }, function (payload, ecModel) { + var newSeriesOptList = []; + zrUtil.each(payload.newOption.series, function (seriesOpt) { + var seriesModel = ecModel.getSeriesByName(seriesOpt.name)[0]; + if (!seriesModel) { + // New created series + // Geuss the series type + newSeriesOptList.push(zrUtil.extend({ + // Default is scatter + type: 'scatter' + }, seriesOpt)); + } + else { + var originalData = seriesModel.get('data'); + newSeriesOptList.push({ + name: seriesOpt.name, + data: tryMergeDataOption(seriesOpt.data, originalData) + }); + } + }); + + ecModel.mergeOption(zrUtil.defaults({ + series: newSeriesOptList + }, payload.newOption)); + }); + + module.exports = DataView; + + +/***/ }, +/* 373 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var zrUtil = __webpack_require__(4); + var BrushController = __webpack_require__(233); + var brushHelper = __webpack_require__(309); + var history = __webpack_require__(374); + + var each = zrUtil.each; + + // Use dataZoomSelect + __webpack_require__(375); + + // Spectial component id start with \0ec\0, see echarts/model/Global.js~hasInnerId + var DATA_ZOOM_ID_BASE = '\0_ec_\0toolbox-dataZoom_'; + + function DataZoom(model, ecModel, api) { + + /** + * @private + * @type {module:echarts/component/helper/BrushController} + */ + (this._brushController = new BrushController(api.getZr())) + .on('brush', zrUtil.bind(this._onBrush, this)) + .mount(); + + /** + * @private + * @type {boolean} + */ + this._isZoomActive; + } + + DataZoom.defaultOption = { + show: true, + // Icon group + icon: { + zoom: 'M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1', + back: 'M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26' + }, + title: { + zoom: '区域缩放', + back: '区域缩放还原' + } + }; + + var proto = DataZoom.prototype; + + proto.render = function (featureModel, ecModel, api, payload) { + this.model = featureModel; + this.ecModel = ecModel; + this.api = api; + + updateZoomBtnStatus(featureModel, ecModel, this, payload); + updateBackBtnStatus(featureModel, ecModel); + }; + + proto.onclick = function (ecModel, api, type) { + handlers[type].call(this); + }; + + proto.remove = function (ecModel, api) { + this._brushController.unmount(); + }; + + proto.dispose = function (ecModel, api) { + this._brushController.dispose(); + }; + + /** + * @private + */ + var handlers = { + + zoom: function () { + var nextActive = !this._isZoomActive; + + this.api.dispatchAction({ + type: 'takeGlobalCursor', + key: 'dataZoomSelect', + dataZoomSelectActive: nextActive + }); + }, + + back: function () { + this._dispatchZoomAction(history.pop(this.ecModel)); + } + }; + + /** + * @private + */ + proto._onBrush = function (areas, opt) { + if (!opt.isEnd || !areas.length) { + return; + } + var snapshot = {}; + var ecModel = this.ecModel; + + this._brushController.updateCovers([]); // remove cover + + var coordInfoList = brushHelper.makeCoordInfoList( + retrieveAxisSetting(this.model.option), ecModel + ); + var rangesCoordInfoList = []; + brushHelper.parseOutputRanges(areas, coordInfoList, ecModel, rangesCoordInfoList); + + var area = areas[0]; // dataZoom can not multiple area. + var coordInfo = rangesCoordInfoList[0]; + var coordRange = area.coordRange; + var brushType = area.brushType; + + if (coordInfo && coordRange) { + if (brushType === 'rect') { + setBatch('xAxis', coordRange[0], coordInfo); + setBatch('yAxis', coordRange[1], coordInfo); + } + else { + var axisNames = {lineX: 'xAxis', lineY: 'yAxis'}; + setBatch(axisNames[brushType], coordRange, coordInfo); + } + } + + history.push(ecModel, snapshot); + + this._dispatchZoomAction(snapshot); + + function setBatch(axisName, minMax, coordInfo) { + var dataZoomModel = findDataZoom(axisName, coordInfo[axisName], ecModel); + if (dataZoomModel) { + snapshot[dataZoomModel.id] = { + dataZoomId: dataZoomModel.id, + startValue: minMax[0], + endValue: minMax[1] + }; + } + } + + function findDataZoom(axisName, axisModel, ecModel) { + var dataZoomModel; + ecModel.eachComponent( + {mainType: 'dataZoom', subType: 'select'}, + function (dzModel, dataZoomIndex) { + var axisIndex = dzModel.get(axisName + 'Index'); + if (axisIndex != null + && ecModel.getComponent(axisName, axisIndex) === axisModel + ) { + dataZoomModel = dzModel; + } + } + ); + return dataZoomModel; + } + }; + + /** + * @private + */ + proto._dispatchZoomAction = function (snapshot) { + var batch = []; + + // Convert from hash map to array. + each(snapshot, function (batchItem, dataZoomId) { + batch.push(zrUtil.clone(batchItem)); + }); + + batch.length && this.api.dispatchAction({ + type: 'dataZoom', + from: this.uid, + batch: batch + }); + }; + + function retrieveAxisSetting(option) { + var setting = {}; + // Compatible with previous setting: null => all axis, false => no axis. + zrUtil.each(['xAxisIndex', 'yAxisIndex'], function (name) { + setting[name] = option[name]; + setting[name] == null && (setting[name] = 'all'); + (setting[name] === false || setting[name] === 'none') && (setting[name] = []); + }); + return setting; + } + + function updateBackBtnStatus(featureModel, ecModel) { + featureModel.setIconStatus( + 'back', + history.count(ecModel) > 1 ? 'emphasis' : 'normal' + ); + } + + function updateZoomBtnStatus(featureModel, ecModel, view, payload) { + var zoomActive = view._isZoomActive; + + if (payload && payload.type === 'takeGlobalCursor') { + zoomActive = payload.key === 'dataZoomSelect' + ? payload.dataZoomSelectActive : false; + } + + view._isZoomActive = zoomActive; + + featureModel.setIconStatus('zoom', zoomActive ? 'emphasis' : 'normal'); + + var coordInfoList = brushHelper.makeCoordInfoList( + retrieveAxisSetting(featureModel.option), ecModel + ); + var brushType = (coordInfoList.xAxisHas && !coordInfoList.yAxisHas) + ? 'lineX' + : (!coordInfoList.xAxisHas && coordInfoList.yAxisHas) + ? 'lineY' + : 'rect'; + + view._brushController + .setPanels(brushHelper.makePanelOpts(coordInfoList)) + .enableBrush( + zoomActive + ? { + brushType: brushType, + brushStyle: { // FIXME user customized? + lineWidth: 0, + // stroke: '#333', + fill: 'rgba(0,0,0,0.2)' + } + } + : false + ); + } + + + __webpack_require__(314).register('dataZoom', DataZoom); + + + // Create special dataZoom option for select + __webpack_require__(1).registerPreprocessor(function (option) { + if (!option) { + return; + } + + var dataZoomOpts = option.dataZoom || (option.dataZoom = []); + if (!zrUtil.isArray(dataZoomOpts)) { + option.dataZoom = dataZoomOpts = [dataZoomOpts]; + } + + var toolboxOpt = option.toolbox; + if (toolboxOpt) { + // Assume there is only one toolbox + if (zrUtil.isArray(toolboxOpt)) { + toolboxOpt = toolboxOpt[0]; + } + + if (toolboxOpt && toolboxOpt.feature) { + var dataZoomOpt = toolboxOpt.feature.dataZoom; + addForAxis('xAxis', dataZoomOpt); + addForAxis('yAxis', dataZoomOpt); + } + } + + function addForAxis(axisName, dataZoomOpt) { + if (!dataZoomOpt) { + return; + } + + // Try not to modify model, because it is not merged yet. + var axisIndicesName = axisName + 'Index'; + var givenAxisIndices = dataZoomOpt[axisIndicesName]; + if (givenAxisIndices != null + && givenAxisIndices != 'all' + && !zrUtil.isArray(givenAxisIndices) + ) { + givenAxisIndices = (givenAxisIndices === false || givenAxisIndices === 'none') ? [] : [givenAxisIndices]; + } + + forEachComponent(axisName, function (axisOpt, axisIndex) { + if (givenAxisIndices != null + && givenAxisIndices != 'all' + && zrUtil.indexOf(givenAxisIndices, axisIndex) === -1 + ) { + return; + } + var newOpt = { + type: 'select', + $fromToolbox: true, + // Id for merge mapping. + id: DATA_ZOOM_ID_BASE + axisName + axisIndex + }; + // FIXME + // Only support one axis now. + newOpt[axisIndicesName] = axisIndex; + dataZoomOpts.push(newOpt); + }); + } + + function forEachComponent(mainType, cb) { + var opts = option[mainType]; + if (!zrUtil.isArray(opts)) { + opts = opts ? [opts] : []; + } + each(opts, cb); + } + }); + + module.exports = DataZoom; + + +/***/ }, +/* 374 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file History manager. + */ + + + var zrUtil = __webpack_require__(4); + var each = zrUtil.each; + + var ATTR = '\0_ec_hist_store'; + + var history = { + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @param {Object} newSnapshot {dataZoomId, batch: [payloadInfo, ...]} + */ + push: function (ecModel, newSnapshot) { + var store = giveStore(ecModel); + + // If previous dataZoom can not be found, + // complete an range with current range. + each(newSnapshot, function (batchItem, dataZoomId) { + var i = store.length - 1; + for (; i >= 0; i--) { + var snapshot = store[i]; + if (snapshot[dataZoomId]) { + break; + } + } + if (i < 0) { + // No origin range set, create one by current range. + var dataZoomModel = ecModel.queryComponents( + {mainType: 'dataZoom', subType: 'select', id: dataZoomId} + )[0]; + if (dataZoomModel) { + var percentRange = dataZoomModel.getPercentRange(); + store[0][dataZoomId] = { + dataZoomId: dataZoomId, + start: percentRange[0], + end: percentRange[1] + }; + } + } + }); + + store.push(newSnapshot); + }, + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @return {Object} snapshot + */ + pop: function (ecModel) { + var store = giveStore(ecModel); + var head = store[store.length - 1]; + store.length > 1 && store.pop(); + + // Find top for all dataZoom. + var snapshot = {}; + each(head, function (batchItem, dataZoomId) { + for (var i = store.length - 1; i >= 0; i--) { + var batchItem = store[i][dataZoomId]; + if (batchItem) { + snapshot[dataZoomId] = batchItem; + break; + } + } + }); + + return snapshot; + }, + + /** + * @public + */ + clear: function (ecModel) { + ecModel[ATTR] = null; + }, + + /** + * @public + * @param {module:echarts/model/Global} ecModel + * @return {number} records. always >= 1. + */ + count: function (ecModel) { + return giveStore(ecModel).length; + } + + }; + + /** + * [{key: dataZoomId, value: {dataZoomId, range}}, ...] + * History length of each dataZoom may be different. + * this._history[0] is used to store origin range. + * @type {Array.} + */ + function giveStore(ecModel) { + var store = ecModel[ATTR]; + if (!store) { + store = ecModel[ATTR] = [{}]; + } + return store; + } + + module.exports = history; + + + +/***/ }, +/* 375 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * DataZoom component entry + */ + + + __webpack_require__(317); + + __webpack_require__(318); + __webpack_require__(321); + + __webpack_require__(376); + __webpack_require__(377); + + __webpack_require__(328); + __webpack_require__(329); + + + +/***/ }, +/* 376 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * @file Data zoom model + */ + + + var DataZoomModel = __webpack_require__(318); + + module.exports = DataZoomModel.extend({ + + type: 'dataZoom.select' + + }); + + + +/***/ }, +/* 377 */ +/***/ function(module, exports, __webpack_require__) { + + + + module.exports = __webpack_require__(321).extend({ + + type: 'dataZoom.select' + + }); + + + +/***/ }, +/* 378 */ +/***/ function(module, exports, __webpack_require__) { + + 'use strict'; + + + var history = __webpack_require__(374); + + function Restore(model) { + this.model = model; + } + + Restore.defaultOption = { + show: true, + icon: 'M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5', + title: '还原' + }; + + var proto = Restore.prototype; + + proto.onclick = function (ecModel, api, type) { + history.clear(ecModel); + + api.dispatchAction({ + type: 'restore', + from: this.uid + }); + }; + + + __webpack_require__(314).register('restore', Restore); + + + __webpack_require__(1).registerAction( + {type: 'restore', event: 'restore', update: 'prepareAndUpdate'}, + function (payload, ecModel) { + ecModel.resetOption('recreate'); + } + ); + + module.exports = Restore; + + +/***/ }, +/* 379 */ +/***/ function(module, exports, __webpack_require__) { + + + __webpack_require__(380); + __webpack_require__(81).registerPainter('vml', __webpack_require__(382)); + + +/***/ }, +/* 380 */ +/***/ function(module, exports, __webpack_require__) { + + // http://www.w3.org/TR/NOTE-VML + // TODO Use proxy like svg instead of overwrite brush methods + + + if (!__webpack_require__(2).canvasSupported) { + var vec2 = __webpack_require__(10); + var BoundingRect = __webpack_require__(9); + var CMD = __webpack_require__(49).CMD; + var colorTool = __webpack_require__(39); + var textContain = __webpack_require__(8); + var RectText = __webpack_require__(48); + var Displayable = __webpack_require__(46); + var ZImage = __webpack_require__(62); + var Text = __webpack_require__(64); + var Path = __webpack_require__(45); + + var Gradient = __webpack_require__(61); + + var vmlCore = __webpack_require__(381); + + var round = Math.round; + var sqrt = Math.sqrt; + var abs = Math.abs; + var cos = Math.cos; + var sin = Math.sin; + var mathMax = Math.max; + + var applyTransform = vec2.applyTransform; + + var comma = ','; + var imageTransformPrefix = 'progid:DXImageTransform.Microsoft'; + + var Z = 21600; + var Z2 = Z / 2; + + var ZLEVEL_BASE = 100000; + var Z_BASE = 1000; + + var initRootElStyle = function (el) { + el.style.cssText = 'position:absolute;left:0;top:0;width:1px;height:1px;'; + el.coordsize = Z + ',' + Z; + el.coordorigin = '0,0'; + }; + + var encodeHtmlAttribute = function (s) { + return String(s).replace(/&/g, '&').replace(/"/g, '"'); + }; + + var rgb2Str = function (r, g, b) { + return 'rgb(' + [r, g, b].join(',') + ')'; + }; + + var append = function (parent, child) { + if (child && parent && child.parentNode !== parent) { + parent.appendChild(child); + } + }; + + var remove = function (parent, child) { + if (child && parent && child.parentNode === parent) { + parent.removeChild(child); + } + }; + + var getZIndex = function (zlevel, z, z2) { + // z 的取值范围为 [0, 1000] + return (parseFloat(zlevel) || 0) * ZLEVEL_BASE + (parseFloat(z) || 0) * Z_BASE + z2; + }; + + var parsePercent = function (value, maxValue) { + if (typeof value === 'string') { + if (value.lastIndexOf('%') >= 0) { + return parseFloat(value) / 100 * maxValue; + } + return parseFloat(value); + } + return value; + }; + + /*************************************************** + * PATH + **************************************************/ + + var setColorAndOpacity = function (el, color, opacity) { + var colorArr = colorTool.parse(color); + opacity = +opacity; + if (isNaN(opacity)) { + opacity = 1; + } + if (colorArr) { + el.color = rgb2Str(colorArr[0], colorArr[1], colorArr[2]); + el.opacity = opacity * colorArr[3]; + } + }; + + var getColorAndAlpha = function (color) { + var colorArr = colorTool.parse(color); + return [ + rgb2Str(colorArr[0], colorArr[1], colorArr[2]), + colorArr[3] + ]; + }; + + var updateFillNode = function (el, style, zrEl) { + // TODO pattern + var fill = style.fill; + if (fill != null) { + // Modified from excanvas + if (fill instanceof Gradient) { + var gradientType; + var angle = 0; + var focus = [0, 0]; + // additional offset + var shift = 0; + // scale factor for offset + var expansion = 1; + var rect = zrEl.getBoundingRect(); + var rectWidth = rect.width; + var rectHeight = rect.height; + if (fill.type === 'linear') { + gradientType = 'gradient'; + var transform = zrEl.transform; + var p0 = [fill.x * rectWidth, fill.y * rectHeight]; + var p1 = [fill.x2 * rectWidth, fill.y2 * rectHeight]; + if (transform) { + applyTransform(p0, p0, transform); + applyTransform(p1, p1, transform); + } + var dx = p1[0] - p0[0]; + var dy = p1[1] - p0[1]; + angle = Math.atan2(dx, dy) * 180 / Math.PI; + // The angle should be a non-negative number. + if (angle < 0) { + angle += 360; + } + + // Very small angles produce an unexpected result because they are + // converted to a scientific notation string. + if (angle < 1e-6) { + angle = 0; + } + } + else { + gradientType = 'gradientradial'; + var p0 = [fill.x * rectWidth, fill.y * rectHeight]; + var transform = zrEl.transform; + var scale = zrEl.scale; + var width = rectWidth; + var height = rectHeight; + focus = [ + // Percent in bounding rect + (p0[0] - rect.x) / width, + (p0[1] - rect.y) / height + ]; + if (transform) { + applyTransform(p0, p0, transform); + } + + width /= scale[0] * Z; + height /= scale[1] * Z; + var dimension = mathMax(width, height); + shift = 2 * 0 / dimension; + expansion = 2 * fill.r / dimension - shift; + } + + // We need to sort the color stops in ascending order by offset, + // otherwise IE won't interpret it correctly. + var stops = fill.colorStops.slice(); + stops.sort(function(cs1, cs2) { + return cs1.offset - cs2.offset; + }); + + var length = stops.length; + // Color and alpha list of first and last stop + var colorAndAlphaList = []; + var colors = []; + for (var i = 0; i < length; i++) { + var stop = stops[i]; + var colorAndAlpha = getColorAndAlpha(stop.color); + colors.push(stop.offset * expansion + shift + ' ' + colorAndAlpha[0]); + if (i === 0 || i === length - 1) { + colorAndAlphaList.push(colorAndAlpha); + } + } + + if (length >= 2) { + var color1 = colorAndAlphaList[0][0]; + var color2 = colorAndAlphaList[1][0]; + var opacity1 = colorAndAlphaList[0][1] * style.opacity; + var opacity2 = colorAndAlphaList[1][1] * style.opacity; + + el.type = gradientType; + el.method = 'none'; + el.focus = '100%'; + el.angle = angle; + el.color = color1; + el.color2 = color2; + el.colors = colors.join(','); + // When colors attribute is used, the meanings of opacity and o:opacity2 + // are reversed. + el.opacity = opacity2; + // FIXME g_o_:opacity ? + el.opacity2 = opacity1; + } + if (gradientType === 'radial') { + el.focusposition = focus.join(','); + } + } + else { + // FIXME Change from Gradient fill to color fill + setColorAndOpacity(el, fill, style.opacity); + } + } + }; + + var updateStrokeNode = function (el, style) { + // if (style.lineJoin != null) { + // el.joinstyle = style.lineJoin; + // } + // if (style.miterLimit != null) { + // el.miterlimit = style.miterLimit * Z; + // } + // if (style.lineCap != null) { + // el.endcap = style.lineCap; + // } + if (style.lineDash != null) { + el.dashstyle = style.lineDash.join(' '); + } + if (style.stroke != null && !(style.stroke instanceof Gradient)) { + setColorAndOpacity(el, style.stroke, style.opacity); + } + }; + + var updateFillAndStroke = function (vmlEl, type, style, zrEl) { + var isFill = type == 'fill'; + var el = vmlEl.getElementsByTagName(type)[0]; + // Stroke must have lineWidth + if (style[type] != null && style[type] !== 'none' && (isFill || (!isFill && style.lineWidth))) { + vmlEl[isFill ? 'filled' : 'stroked'] = 'true'; + // FIXME Remove before updating, or set `colors` will throw error + if (style[type] instanceof Gradient) { + remove(vmlEl, el); + } + if (!el) { + el = vmlCore.createNode(type); + } + + isFill ? updateFillNode(el, style, zrEl) : updateStrokeNode(el, style); + append(vmlEl, el); + } + else { + vmlEl[isFill ? 'filled' : 'stroked'] = 'false'; + remove(vmlEl, el); + } + }; + + var points = [[], [], []]; + var pathDataToString = function (data, m) { + var M = CMD.M; + var C = CMD.C; + var L = CMD.L; + var A = CMD.A; + var Q = CMD.Q; + + var str = []; + var nPoint; + var cmdStr; + var cmd; + var i; + var xi; + var yi; + for (i = 0; i < data.length;) { + cmd = data[i++]; + cmdStr = ''; + nPoint = 0; + switch (cmd) { + case M: + cmdStr = ' m '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points[0][0] = xi; + points[0][1] = yi; + break; + case L: + cmdStr = ' l '; + nPoint = 1; + xi = data[i++]; + yi = data[i++]; + points[0][0] = xi; + points[0][1] = yi; + break; + case Q: + case C: + cmdStr = ' c '; + nPoint = 3; + var x1 = data[i++]; + var y1 = data[i++]; + var x2 = data[i++]; + var y2 = data[i++]; + var x3; + var y3; + if (cmd === Q) { + // Convert quadratic to cubic using degree elevation + x3 = x2; + y3 = y2; + x2 = (x2 + 2 * x1) / 3; + y2 = (y2 + 2 * y1) / 3; + x1 = (xi + 2 * x1) / 3; + y1 = (yi + 2 * y1) / 3; + } + else { + x3 = data[i++]; + y3 = data[i++]; + } + points[0][0] = x1; + points[0][1] = y1; + points[1][0] = x2; + points[1][1] = y2; + points[2][0] = x3; + points[2][1] = y3; + + xi = x3; + yi = y3; + break; + case A: + var x = 0; + var y = 0; + var sx = 1; + var sy = 1; + var angle = 0; + if (m) { + // Extract SRT from matrix + x = m[4]; + y = m[5]; + sx = sqrt(m[0] * m[0] + m[1] * m[1]); + sy = sqrt(m[2] * m[2] + m[3] * m[3]); + angle = Math.atan2(-m[1] / sy, m[0] / sx); + } + + var cx = data[i++]; + var cy = data[i++]; + var rx = data[i++]; + var ry = data[i++]; + var startAngle = data[i++] + angle; + var endAngle = data[i++] + startAngle + angle; + // FIXME + // var psi = data[i++]; + i++; + var clockwise = data[i++]; + + var x0 = cx + cos(startAngle) * rx; + var y0 = cy + sin(startAngle) * ry; + + var x1 = cx + cos(endAngle) * rx; + var y1 = cy + sin(endAngle) * ry; + + var type = clockwise ? ' wa ' : ' at '; + if (Math.abs(x0 - x1) < 1e-10) { + // IE won't render arches drawn counter clockwise if x0 == x1. + if (Math.abs(endAngle - startAngle) > 1e-2) { + // Offset x0 by 1/80 of a pixel. Use something + // that can be represented in binary + if (clockwise) { + x0 += 270 / Z; + } + } + else { + // Avoid case draw full circle + if (Math.abs(y0 - cy) < 1e-10) { + if ((clockwise && x0 < cx) || (!clockwise && x0 > cx)) { + y1 -= 270 / Z; + } + else { + y1 += 270 / Z; + } + } + else if ((clockwise && y0 < cy) || (!clockwise && y0 > cy)) { + x1 += 270 / Z; + } + else { + x1 -= 270 / Z; + } + } + } + str.push( + type, + round(((cx - rx) * sx + x) * Z - Z2), comma, + round(((cy - ry) * sy + y) * Z - Z2), comma, + round(((cx + rx) * sx + x) * Z - Z2), comma, + round(((cy + ry) * sy + y) * Z - Z2), comma, + round((x0 * sx + x) * Z - Z2), comma, + round((y0 * sy + y) * Z - Z2), comma, + round((x1 * sx + x) * Z - Z2), comma, + round((y1 * sy + y) * Z - Z2) + ); + + xi = x1; + yi = y1; + break; + case CMD.R: + var p0 = points[0]; + var p1 = points[1]; + // x0, y0 + p0[0] = data[i++]; + p0[1] = data[i++]; + // x1, y1 + p1[0] = p0[0] + data[i++]; + p1[1] = p0[1] + data[i++]; + + if (m) { + applyTransform(p0, p0, m); + applyTransform(p1, p1, m); + } + + p0[0] = round(p0[0] * Z - Z2); + p1[0] = round(p1[0] * Z - Z2); + p0[1] = round(p0[1] * Z - Z2); + p1[1] = round(p1[1] * Z - Z2); + str.push( + // x0, y0 + ' m ', p0[0], comma, p0[1], + // x1, y0 + ' l ', p1[0], comma, p0[1], + // x1, y1 + ' l ', p1[0], comma, p1[1], + // x0, y1 + ' l ', p0[0], comma, p1[1] + ); + break; + case CMD.Z: + // FIXME Update xi, yi + str.push(' x '); + } + + if (nPoint > 0) { + str.push(cmdStr); + for (var k = 0; k < nPoint; k++) { + var p = points[k]; + + m && applyTransform(p, p, m); + // 不 round 会非常慢 + str.push( + round(p[0] * Z - Z2), comma, round(p[1] * Z - Z2), + k < nPoint - 1 ? comma : '' + ); + } + } + } + + return str.join(''); + }; + + // Rewrite the original path method + Path.prototype.brushVML = function (vmlRoot) { + var style = this.style; + + var vmlEl = this._vmlEl; + if (!vmlEl) { + vmlEl = vmlCore.createNode('shape'); + initRootElStyle(vmlEl); + + this._vmlEl = vmlEl; + } + + updateFillAndStroke(vmlEl, 'fill', style, this); + updateFillAndStroke(vmlEl, 'stroke', style, this); + + var m = this.transform; + var needTransform = m != null; + var strokeEl = vmlEl.getElementsByTagName('stroke')[0]; + if (strokeEl) { + var lineWidth = style.lineWidth; + // Get the line scale. + // Determinant of this.m_ means how much the area is enlarged by the + // transformation. So its square root can be used as a scale factor + // for width. + if (needTransform && !style.strokeNoScale) { + var det = m[0] * m[3] - m[1] * m[2]; + lineWidth *= sqrt(abs(det)); + } + strokeEl.weight = lineWidth + 'px'; + } + + var path = this.path; + if (this.__dirtyPath) { + path.beginPath(); + this.buildPath(path, this.shape); + path.toStatic(); + this.__dirtyPath = false; + } + + vmlEl.path = pathDataToString(path.data, this.transform); + + vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Append to root + append(vmlRoot, vmlEl); + + // Text + if (style.text) { + this.drawRectText(vmlRoot, this.getBoundingRect()); + } + else { + this.removeRectText(vmlRoot); + } + }; + + Path.prototype.onRemove = function (vmlRoot) { + remove(vmlRoot, this._vmlEl); + this.removeRectText(vmlRoot); + }; + + Path.prototype.onAdd = function (vmlRoot) { + append(vmlRoot, this._vmlEl); + this.appendRectText(vmlRoot); + }; + + /*************************************************** + * IMAGE + **************************************************/ + var isImage = function (img) { + // FIXME img instanceof Image 如果 img 是一个字符串的时候,IE8 下会报错 + return (typeof img === 'object') && img.tagName && img.tagName.toUpperCase() === 'IMG'; + // return img instanceof Image; + }; + + // Rewrite the original path method + ZImage.prototype.brushVML = function (vmlRoot) { + var style = this.style; + var image = style.image; + + // Image original width, height + var ow; + var oh; + + if (isImage(image)) { + var src = image.src; + if (src === this._imageSrc) { + ow = this._imageWidth; + oh = this._imageHeight; + } + else { + var imageRuntimeStyle = image.runtimeStyle; + var oldRuntimeWidth = imageRuntimeStyle.width; + var oldRuntimeHeight = imageRuntimeStyle.height; + imageRuntimeStyle.width = 'auto'; + imageRuntimeStyle.height = 'auto'; + + // get the original size + ow = image.width; + oh = image.height; + + // and remove overides + imageRuntimeStyle.width = oldRuntimeWidth; + imageRuntimeStyle.height = oldRuntimeHeight; + + // Caching image original width, height and src + this._imageSrc = src; + this._imageWidth = ow; + this._imageHeight = oh; + } + image = src; + } + else { + if (image === this._imageSrc) { + ow = this._imageWidth; + oh = this._imageHeight; + } + } + if (!image) { + return; + } + + var x = style.x || 0; + var y = style.y || 0; + + var dw = style.width; + var dh = style.height; + + var sw = style.sWidth; + var sh = style.sHeight; + var sx = style.sx || 0; + var sy = style.sy || 0; + + var hasCrop = sw && sh; + + var vmlEl = this._vmlEl; + if (!vmlEl) { + // FIXME 使用 group 在 left, top 都不是 0 的时候就无法显示了。 + // vmlEl = vmlCore.createNode('group'); + vmlEl = vmlCore.doc.createElement('div'); + initRootElStyle(vmlEl); + + this._vmlEl = vmlEl; + } + + var vmlElStyle = vmlEl.style; + var hasRotation = false; + var m; + var scaleX = 1; + var scaleY = 1; + if (this.transform) { + m = this.transform; + scaleX = sqrt(m[0] * m[0] + m[1] * m[1]); + scaleY = sqrt(m[2] * m[2] + m[3] * m[3]); + + hasRotation = m[1] || m[2]; + } + if (hasRotation) { + // If filters are necessary (rotation exists), create them + // filters are bog-slow, so only create them if abbsolutely necessary + // The following check doesn't account for skews (which don't exist + // in the canvas spec (yet) anyway. + // From excanvas + var p0 = [x, y]; + var p1 = [x + dw, y]; + var p2 = [x, y + dh]; + var p3 = [x + dw, y + dh]; + applyTransform(p0, p0, m); + applyTransform(p1, p1, m); + applyTransform(p2, p2, m); + applyTransform(p3, p3, m); + + var maxX = mathMax(p0[0], p1[0], p2[0], p3[0]); + var maxY = mathMax(p0[1], p1[1], p2[1], p3[1]); + + var transformFilter = []; + transformFilter.push('M11=', m[0] / scaleX, comma, + 'M12=', m[2] / scaleY, comma, + 'M21=', m[1] / scaleX, comma, + 'M22=', m[3] / scaleY, comma, + 'Dx=', round(x * scaleX + m[4]), comma, + 'Dy=', round(y * scaleY + m[5])); + + vmlElStyle.padding = '0 ' + round(maxX) + 'px ' + round(maxY) + 'px 0'; + // FIXME DXImageTransform 在 IE11 的兼容模式下不起作用 + vmlElStyle.filter = imageTransformPrefix + '.Matrix(' + + transformFilter.join('') + ', SizingMethod=clip)'; + + } + else { + if (m) { + x = x * scaleX + m[4]; + y = y * scaleY + m[5]; + } + vmlElStyle.filter = ''; + vmlElStyle.left = round(x) + 'px'; + vmlElStyle.top = round(y) + 'px'; + } + + var imageEl = this._imageEl; + var cropEl = this._cropEl; + + if (!imageEl) { + imageEl = vmlCore.doc.createElement('div'); + this._imageEl = imageEl; + } + var imageELStyle = imageEl.style; + if (hasCrop) { + // Needs know image original width and height + if (! (ow && oh)) { + var tmpImage = new Image(); + var self = this; + tmpImage.onload = function () { + tmpImage.onload = null; + ow = tmpImage.width; + oh = tmpImage.height; + // Adjust image width and height to fit the ratio destinationSize / sourceSize + imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; + + // Caching image original width, height and src + self._imageWidth = ow; + self._imageHeight = oh; + self._imageSrc = image; + }; + tmpImage.src = image; + } + else { + imageELStyle.width = round(scaleX * ow * dw / sw) + 'px'; + imageELStyle.height = round(scaleY * oh * dh / sh) + 'px'; + } + + if (! cropEl) { + cropEl = vmlCore.doc.createElement('div'); + cropEl.style.overflow = 'hidden'; + this._cropEl = cropEl; + } + var cropElStyle = cropEl.style; + cropElStyle.width = round((dw + sx * dw / sw) * scaleX); + cropElStyle.height = round((dh + sy * dh / sh) * scaleY); + cropElStyle.filter = imageTransformPrefix + '.Matrix(Dx=' + + (-sx * dw / sw * scaleX) + ',Dy=' + (-sy * dh / sh * scaleY) + ')'; + + if (! cropEl.parentNode) { + vmlEl.appendChild(cropEl); + } + if (imageEl.parentNode != cropEl) { + cropEl.appendChild(imageEl); + } + } + else { + imageELStyle.width = round(scaleX * dw) + 'px'; + imageELStyle.height = round(scaleY * dh) + 'px'; + + vmlEl.appendChild(imageEl); + + if (cropEl && cropEl.parentNode) { + vmlEl.removeChild(cropEl); + this._cropEl = null; + } + } + + var filterStr = ''; + var alpha = style.opacity; + if (alpha < 1) { + filterStr += '.Alpha(opacity=' + round(alpha * 100) + ') '; + } + filterStr += imageTransformPrefix + '.AlphaImageLoader(src=' + image + ', SizingMethod=scale)'; + + imageELStyle.filter = filterStr; + + vmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Append to root + append(vmlRoot, vmlEl); + + // Text + if (style.text) { + this.drawRectText(vmlRoot, this.getBoundingRect()); + } + }; + + ZImage.prototype.onRemove = function (vmlRoot) { + remove(vmlRoot, this._vmlEl); + + this._vmlEl = null; + this._cropEl = null; + this._imageEl = null; + + this.removeRectText(vmlRoot); + }; + + ZImage.prototype.onAdd = function (vmlRoot) { + append(vmlRoot, this._vmlEl); + this.appendRectText(vmlRoot); + }; + + + /*************************************************** + * TEXT + **************************************************/ + + var DEFAULT_STYLE_NORMAL = 'normal'; + + var fontStyleCache = {}; + var fontStyleCacheCount = 0; + var MAX_FONT_CACHE_SIZE = 100; + var fontEl = document.createElement('div'); + + var getFontStyle = function (fontString) { + var fontStyle = fontStyleCache[fontString]; + if (!fontStyle) { + // Clear cache + if (fontStyleCacheCount > MAX_FONT_CACHE_SIZE) { + fontStyleCacheCount = 0; + fontStyleCache = {}; + } + + var style = fontEl.style; + var fontFamily; + try { + style.font = fontString; + fontFamily = style.fontFamily.split(',')[0]; + } + catch (e) { + } + + fontStyle = { + style: style.fontStyle || DEFAULT_STYLE_NORMAL, + variant: style.fontVariant || DEFAULT_STYLE_NORMAL, + weight: style.fontWeight || DEFAULT_STYLE_NORMAL, + size: parseFloat(style.fontSize || 12) | 0, + family: fontFamily || 'Microsoft YaHei' + }; + + fontStyleCache[fontString] = fontStyle; + fontStyleCacheCount++; + } + return fontStyle; + }; + + var textMeasureEl; + // Overwrite measure text method + textContain.measureText = function (text, textFont) { + var doc = vmlCore.doc; + if (!textMeasureEl) { + textMeasureEl = doc.createElement('div'); + textMeasureEl.style.cssText = 'position:absolute;top:-20000px;left:0;' + + 'padding:0;margin:0;border:none;white-space:pre;'; + vmlCore.doc.body.appendChild(textMeasureEl); + } + + try { + textMeasureEl.style.font = textFont; + } catch (ex) { + // Ignore failures to set to invalid font. + } + textMeasureEl.innerHTML = ''; + // Don't use innerHTML or innerText because they allow markup/whitespace. + textMeasureEl.appendChild(doc.createTextNode(text)); + return { + width: textMeasureEl.offsetWidth + }; + }; + + var tmpRect = new BoundingRect(); + + var drawRectText = function (vmlRoot, rect, textRect, fromTextEl) { + + var style = this.style; + var text = style.text; + if (!text) { + return; + } + + var x; + var y; + var align = style.textAlign; + var fontStyle = getFontStyle(style.textFont); + // FIXME encodeHtmlAttribute ? + var font = fontStyle.style + ' ' + fontStyle.variant + ' ' + fontStyle.weight + ' ' + + fontStyle.size + 'px "' + fontStyle.family + '"'; + + var baseline = style.textBaseline; + var verticalAlign = style.textVerticalAlign; + + textRect = textRect || textContain.getBoundingRect(text, font, align, baseline); + + // Transform rect to view space + var m = this.transform; + // Ignore transform for text in other element + if (m && !fromTextEl) { + tmpRect.copy(rect); + tmpRect.applyTransform(m); + rect = tmpRect; + } + + if (!fromTextEl) { + var textPosition = style.textPosition; + var distance = style.textDistance; + // Text position represented by coord + if (textPosition instanceof Array) { + x = rect.x + parsePercent(textPosition[0], rect.width); + y = rect.y + parsePercent(textPosition[1], rect.height); + + align = align || 'left'; + baseline = baseline || 'top'; + } + else { + var res = textContain.adjustTextPositionOnRect( + textPosition, rect, textRect, distance + ); + x = res.x; + y = res.y; + + // Default align and baseline when has textPosition + align = align || res.textAlign; + baseline = baseline || res.textBaseline; + } + } + else { + x = rect.x; + y = rect.y; + } + if (verticalAlign) { + switch (verticalAlign) { + case 'middle': + y -= textRect.height / 2; + break; + case 'bottom': + y -= textRect.height; + break; + // 'top' + } + // Ignore baseline + baseline = 'top'; + } + + var fontSize = fontStyle.size; + // 1.75 is an arbitrary number, as there is no info about the text baseline + switch (baseline) { + case 'hanging': + case 'top': + y += fontSize / 1.75; + break; + case 'middle': + break; + default: + // case null: + // case 'alphabetic': + // case 'ideographic': + // case 'bottom': + y -= fontSize / 2.25; + break; + } + switch (align) { + case 'left': + break; + case 'center': + x -= textRect.width / 2; + break; + case 'right': + x -= textRect.width; + break; + // case 'end': + // align = elementStyle.direction == 'ltr' ? 'right' : 'left'; + // break; + // case 'start': + // align = elementStyle.direction == 'rtl' ? 'right' : 'left'; + // break; + // default: + // align = 'left'; + } + + var createNode = vmlCore.createNode; + + var textVmlEl = this._textVmlEl; + var pathEl; + var textPathEl; + var skewEl; + if (!textVmlEl) { + textVmlEl = createNode('line'); + pathEl = createNode('path'); + textPathEl = createNode('textpath'); + skewEl = createNode('skew'); + + // FIXME Why here is not cammel case + // Align 'center' seems wrong + textPathEl.style['v-text-align'] = 'left'; + + initRootElStyle(textVmlEl); + + pathEl.textpathok = true; + textPathEl.on = true; + + textVmlEl.from = '0 0'; + textVmlEl.to = '1000 0.05'; + + append(textVmlEl, skewEl); + append(textVmlEl, pathEl); + append(textVmlEl, textPathEl); + + this._textVmlEl = textVmlEl; + } + else { + // 这里是在前面 appendChild 保证顺序的前提下 + skewEl = textVmlEl.firstChild; + pathEl = skewEl.nextSibling; + textPathEl = pathEl.nextSibling; + } + + var coords = [x, y]; + var textVmlElStyle = textVmlEl.style; + // Ignore transform for text in other element + if (m && fromTextEl) { + applyTransform(coords, coords, m); + + skewEl.on = true; + + skewEl.matrix = m[0].toFixed(3) + comma + m[2].toFixed(3) + comma + + m[1].toFixed(3) + comma + m[3].toFixed(3) + ',0,0'; + + // Text position + skewEl.offset = (round(coords[0]) || 0) + ',' + (round(coords[1]) || 0); + // Left top point as origin + skewEl.origin = '0 0'; + + textVmlElStyle.left = '0px'; + textVmlElStyle.top = '0px'; + } + else { + skewEl.on = false; + textVmlElStyle.left = round(x) + 'px'; + textVmlElStyle.top = round(y) + 'px'; + } + + textPathEl.string = encodeHtmlAttribute(text); + // TODO + try { + textPathEl.style.font = font; + } + // Error font format + catch (e) {} + + updateFillAndStroke(textVmlEl, 'fill', { + fill: fromTextEl ? style.fill : style.textFill, + opacity: style.opacity + }, this); + updateFillAndStroke(textVmlEl, 'stroke', { + stroke: fromTextEl ? style.stroke : style.textStroke, + opacity: style.opacity, + lineDash: style.lineDash + }, this); + + textVmlEl.style.zIndex = getZIndex(this.zlevel, this.z, this.z2); + + // Attached to root + append(vmlRoot, textVmlEl); + }; + + var removeRectText = function (vmlRoot) { + remove(vmlRoot, this._textVmlEl); + this._textVmlEl = null; + }; + + var appendRectText = function (vmlRoot) { + append(vmlRoot, this._textVmlEl); + }; + + var list = [RectText, Displayable, ZImage, Path, Text]; + + // In case Displayable has been mixed in RectText + for (var i = 0; i < list.length; i++) { + var proto = list[i].prototype; + proto.drawRectText = drawRectText; + proto.removeRectText = removeRectText; + proto.appendRectText = appendRectText; + } + + Text.prototype.brushVML = function (vmlRoot) { + var style = this.style; + if (style.text) { + this.drawRectText(vmlRoot, { + x: style.x || 0, y: style.y || 0, + width: 0, height: 0 + }, this.getBoundingRect(), true); + } + else { + this.removeRectText(vmlRoot); + } + }; + + Text.prototype.onRemove = function (vmlRoot) { + this.removeRectText(vmlRoot); + }; + + Text.prototype.onAdd = function (vmlRoot) { + this.appendRectText(vmlRoot); + }; + } + + +/***/ }, +/* 381 */ +/***/ function(module, exports, __webpack_require__) { + + + + if (!__webpack_require__(2).canvasSupported) { + var urn = 'urn:schemas-microsoft-com:vml'; + + var createNode; + var win = window; + var doc = win.document; + + var vmlInited = false; + + try { + !doc.namespaces.zrvml && doc.namespaces.add('zrvml', urn); + createNode = function (tagName) { + return doc.createElement(''); + }; + } + catch (e) { + createNode = function (tagName) { + return doc.createElement('<' + tagName + ' xmlns="' + urn + '" class="zrvml">'); + }; + } + + // From raphael + var initVML = function () { + if (vmlInited) { + return; + } + vmlInited = true; + + var styleSheets = doc.styleSheets; + if (styleSheets.length < 31) { + doc.createStyleSheet().addRule('.zrvml', 'behavior:url(#default#VML)'); + } + else { + // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx + styleSheets[0].addRule('.zrvml', 'behavior:url(#default#VML)'); + } + }; + + // Not useing return to avoid error when converting to CommonJS module + module.exports = { + doc: doc, + initVML: initVML, + createNode: createNode + }; + } + + +/***/ }, +/* 382 */ +/***/ function(module, exports, __webpack_require__) { + + /** + * VML Painter. + * + * @module zrender/vml/Painter + */ + + + + var zrLog = __webpack_require__(40); + var vmlCore = __webpack_require__(381); + + function parseInt10(val) { + return parseInt(val, 10); + } + + /** + * @alias module:zrender/vml/Painter + */ + function VMLPainter(root, storage) { + + vmlCore.initVML(); + + this.root = root; + + this.storage = storage; + + var vmlViewport = document.createElement('div'); + + var vmlRoot = document.createElement('div'); + + vmlViewport.style.cssText = 'display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;'; + + vmlRoot.style.cssText = 'position:absolute;left:0;top:0;'; + + root.appendChild(vmlViewport); + + this._vmlRoot = vmlRoot; + this._vmlViewport = vmlViewport; + + this.resize(); + + // Modify storage + var oldDelFromMap = storage.delFromMap; + var oldAddToMap = storage.addToMap; + storage.delFromMap = function (elId) { + var el = storage.get(elId); + + oldDelFromMap.call(storage, elId); + + if (el) { + el.onRemove && el.onRemove(vmlRoot); + } + }; + + storage.addToMap = function (el) { + // Displayable already has a vml node + el.onAdd && el.onAdd(vmlRoot); + + oldAddToMap.call(storage, el); + }; + + this._firstPaint = true; + } + + VMLPainter.prototype = { + + constructor: VMLPainter, + + /** + * @return {HTMLDivElement} + */ + getViewportRoot: function () { + return this._vmlViewport; + }, + + /** + * 刷新 + */ + refresh: function () { + + var list = this.storage.getDisplayList(true, true); + + this._paintList(list); + }, + + _paintList: function (list) { + var vmlRoot = this._vmlRoot; + for (var i = 0; i < list.length; i++) { + var el = list[i]; + if (el.invisible || el.ignore) { + if (!el.__alreadyNotVisible) { + el.onRemove(vmlRoot); + } + // Set as already invisible + el.__alreadyNotVisible = true; + } + else { + if (el.__alreadyNotVisible) { + el.onAdd(vmlRoot); + } + el.__alreadyNotVisible = false; + if (el.__dirty) { + el.beforeBrush && el.beforeBrush(); + (el.brushVML || el.brush).call(el, vmlRoot); + el.afterBrush && el.afterBrush(); + } + } + el.__dirty = false; + } + + if (this._firstPaint) { + // Detached from document at first time + // to avoid page refreshing too many times + + // FIXME 如果每次都先 removeChild 可能会导致一些填充和描边的效果改变 + this._vmlViewport.appendChild(vmlRoot); + this._firstPaint = false; + } + }, + + resize: function () { + var width = this._getWidth(); + var height = this._getHeight(); + + if (this._width != width && this._height != height) { + this._width = width; + this._height = height; + + var vmlViewportStyle = this._vmlViewport.style; + vmlViewportStyle.width = width + 'px'; + vmlViewportStyle.height = height + 'px'; + } + }, + + dispose: function () { + this.root.innerHTML = ''; + + this._vmlRoot = + this._vmlViewport = + this.storage = null; + }, + + getWidth: function () { + return this._width; + }, + + getHeight: function () { + return this._height; + }, + + clear: function () { + this.root.removeChild(this.vmlViewport); + }, + + _getWidth: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientWidth || parseInt10(stl.width)) + - parseInt10(stl.paddingLeft) + - parseInt10(stl.paddingRight)) | 0; + }, + + _getHeight: function () { + var root = this.root; + var stl = root.currentStyle; + + return ((root.clientHeight || parseInt10(stl.height)) + - parseInt10(stl.paddingTop) + - parseInt10(stl.paddingBottom)) | 0; + } + }; + + // Not supported methods + function createMethodNotSupport(method) { + return function () { + zrLog('In IE8.0 VML mode painter not support method "' + method + '"'); + }; + } + + var notSupportedMethods = [ + 'getLayer', 'insertLayer', 'eachLayer', 'eachBuildinLayer', 'eachOtherLayer', 'getLayers', + 'modLayer', 'delLayer', 'clearLayer', 'toDataURL', 'pathToImage' + ]; + + for (var i = 0; i < notSupportedMethods.length; i++) { + var name = notSupportedMethods[i]; + VMLPainter.prototype[name] = createMethodNotSupport(name); + } + + module.exports = VMLPainter; + + +/***/ } +/******/ ]) +}); +; \ No newline at end of file diff --git a/src/main/webapp/static/macarons.js b/src/main/webapp/static/macarons.js new file mode 100644 index 0000000..c879125 --- /dev/null +++ b/src/main/webapp/static/macarons.js @@ -0,0 +1,198 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['exports', './echarts'], factory); + } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { + // CommonJS + factory(exports, require('echarts')); + } else { + // Browser globals + factory({}, root.echarts); + } +}(this, function (exports, echarts) { + var log = function (msg) { + if (typeof console !== 'undefined') { + console && console.error && console.error(msg); + } + }; + if (!echarts) { + log('ECharts is not Loaded'); + return; + } + + var colorPalette = [ + '#2ec7c9','#b6a2de','#5ab1ef','#ffb980','#d87a80', + '#8d98b3','#e5cf0d','#97b552','#95706d','#dc69aa', + '#07a2a4','#9a7fd1','#588dd5','#f5994e','#c05050', + '#59678c','#c9ab00','#7eb00a','#6f5553','#c14089' + ]; + + + var theme = { + color: colorPalette, + + title: { + textStyle: { + fontWeight: 'normal', + color: '#008acd' + } + }, + + visualMap: { + itemWidth: 15, + color: ['#5ab1ef','#e0ffff'] + }, + + toolbox: { + iconStyle: { + normal: { + borderColor: colorPalette[0] + } + } + }, + + tooltip: { + backgroundColor: 'rgba(50,50,50,0.5)', + axisPointer : { + type : 'line', + lineStyle : { + color: '#008acd' + }, + crossStyle: { + color: '#008acd' + }, + shadowStyle : { + color: 'rgba(200,200,200,0.2)' + } + } + }, + + dataZoom: { + dataBackgroundColor: '#efefff', + fillerColor: 'rgba(182,162,222,0.2)', + handleColor: '#008acd' + }, + + grid: { + borderColor: '#eee' + }, + + categoryAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + valueAxis: { + axisLine: { + lineStyle: { + color: '#008acd' + } + }, + splitArea : { + show : true, + areaStyle : { + color: ['rgba(250,250,250,0.1)','rgba(200,200,200,0.1)'] + } + }, + splitLine: { + lineStyle: { + color: ['#eee'] + } + } + }, + + timeline : { + lineStyle : { + color : '#008acd' + }, + controlStyle : { + normal : { color : '#008acd'}, + emphasis : { color : '#008acd'} + }, + symbol : 'emptyCircle', + symbolSize : 3 + }, + + line: { + smooth : true, + symbol: 'emptyCircle', + symbolSize: 3 + }, + + candlestick: { + itemStyle: { + normal: { + color: '#d87a80', + color0: '#2ec7c9', + lineStyle: { + color: '#d87a80', + color0: '#2ec7c9' + } + } + } + }, + + scatter: { + symbol: 'circle', + symbolSize: 4 + }, + + map: { + label: { + normal: { + textStyle: { + color: '#d87a80' + } + } + }, + itemStyle: { + normal: { + borderColor: '#eee', + areaColor: '#ddd' + }, + emphasis: { + areaColor: '#fe994e' + } + } + }, + + graph: { + color: colorPalette + }, + + gauge : { + axisLine: { + lineStyle: { + color: [[0.2, '#2ec7c9'],[0.8, '#5ab1ef'],[1, '#d87a80']], + width: 10 + } + }, + axisTick: { + splitNumber: 10, + length :15, + lineStyle: { + color: 'auto' + } + }, + splitLine: { + length :22, + lineStyle: { + color: 'auto' + } + }, + pointer : { + width : 5 + } + } + }; + + echarts.registerTheme('macarons', theme); +})); \ No newline at end of file